// #!/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 fud_stmt, is_fart, 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, 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_infix, 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_prefix, 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 = "v2022.3.30"; 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 === 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(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) { 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; // 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_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_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 + ".