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 shift, signature, single, slice, some, sort, source, spawn, splice, split,
129. 1 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.3.26";
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. 1926function assertOrThrow(condition, message) {
308. 1926
309. 1926// This function will throw <message> if <condition> is falsy.
310. 1926
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. 1926}
319. 1
320. 93854function empty() {
321. 93854
322. 93854// The empty function produces a new empty object that inherits nothing. This is
323. 93854// much better than '{}' because confusions around accidental method names like
324. 93854// 'constructor' are completely avoided.
325. 93854
326. 93854 return Object.create(null);
327. 93854}
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. 14189function htmlEscape(str) {
575. 14189
576. 14189// This function will make <str> html-safe by escaping & < >.
577. 14189
578. 14189 return String(str).replace((
579. 14189 /&/g
580. 14189 ), "&").replace((
581. 14189 /</g
582. 14189 ), "<").replace((
583. 14189 />/g
584. 14189 ), ">");
585. 14189}
586. 1
587. 666function jslint(
588. 666 source = "", // A text to analyze.
589. 666 option_dict = empty(), // An object whose keys correspond to option
590. 666 // ... names.
591. 666 global_list = [] // An array of strings containing global
592. 666 // ... variables that the file is allowed
593. 666 // ... readonly access.
594. 666) {
595. 666
596. 666// The jslint function itself.
597. 666
598. 666 let catch_list = []; // The array containing all catch-blocks.
599. 666 let catch_stack = [ // The stack of catch-blocks.
600. 666 {
601. 666 context: empty()
602. 666 }
603. 666 ];
604. 666 let cause_dict = empty(); // The object of test-causes.
605. 666 let directive_list = []; // The directive comments.
606. 666 let export_dict = empty(); // The exported names and values.
607. 666 let function_list = []; // The array containing all functions.
608. 666 let function_stack = []; // The stack of functions.
609. 666 let global_dict = empty(); // The object containing the global
610. 666 // ... declarations.
611. 666 let import_list = []; // The array collecting all import-from strings.
612. 666 let line_list = String( // The array containing source lines.
613. 666 "\n" + source
614. 105205 ).split(jslint_rgx_crlf).map(function (line_source) {
615. 105205 return {
616. 105205 line_source
617. 105205 };
618. 105205 });
619. 666 let mode_stop = false; // true if JSLint cannot finish.
620. 666 let property_dict = empty(); // The object containing the tallied
621. 666 // ... property names.
622. 666 let state = empty(); // jslint state-object to be passed between
623. 666 // jslint functions.
624. 666 let syntax_dict = empty(); // The object containing the parser.
625. 666 let tenure = empty(); // The predefined property registry.
626. 666 let token_global = { // The global object; the outermost context.
627. 666 async: 0,
628. 666 body: true,
629. 666 context: empty(),
630. 666 finally: 0,
631. 666 from: 0,
632. 666 id: "(global)",
633. 666 level: 0,
634. 666 line: jslint_fudge,
635. 666 live: [],
636. 666 loop: 0,
637. 666 switch: 0,
638. 666 thru: 0,
639. 666 try: 0
640. 666 };
641. 666 let token_list = []; // The array of tokens.
642. 666 let warning_list = []; // The array collecting all generated warnings.
643. 666
644. 666// Error reportage functions:
645. 666
646. 8028 function artifact(the_token) {
647. 8028
648. 8028// Return a string representing an artifact.
649. 8028
650. 254 the_token = the_token || state.token_nxt;
651. 8028 return (
652. 5253 (the_token.id === "(string)" || the_token.id === "(number)")
653. 2900 ? String(the_token.value)
654. 5128 : the_token.id
655. 8028 );
656. 8028 }
657. 666
658. 31933 function is_equal(aa, bb) {
659. 31933
660. 31933// test_cause:
661. 31933// ["0&&0", "is_equal", "", "", 0]
662. 31933
663. 31933 test_cause("");
664. 31933
665. 31933// Probably deadcode.
666. 31933// if (aa === bb) {
667. 31933// return true;
668. 31933// }
669. 31933
670. 31933 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. 31906 }
686. 31906
687. 31906// Probably deadcode.
688. 31906// if (Array.isArray(bb)) {
689. 31906// return false;
690. 31906// }
691. 31906
692. 31906 jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
693. 31906 switch (aa.id === bb.id && aa.id) {
694. 65 case "(number)":
695. 23424 case "(string)":
696. 23424 return aa.value === bb.value;
697. 31933
698. 31933// PR-394 - Bugfix
699. 31933// Fix jslint falsely believing megastring literals `0` and `1` are similar.
700. 31933
701. 15 case "`":
702. 15 if (!is_equal(aa.value, bb.value)) {
703. 15 return false;
704. 15 }
705. 15 break;
706. 8479 }
707. 8479 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. 8445 }
715. 8445 if (aa.arity === bb.arity && aa.id === bb.id) {
716. 2121 if (aa.id === "." || aa.id === "?.") {
717. 2121
718. 2121// test_cause:
719. 2121// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0]
720. 2121// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0]
721. 2121
722. 2121 test_cause("recurse_arity_id");
723. 2121 return (
724. 2121 is_equal(aa.expression, bb.expression)
725. 2121 && is_equal(aa.name, bb.name)
726. 2121 );
727. 2121 }
728. 2121 if (aa.arity === "unary") {
729. 2121
730. 2121// test_cause:
731. 2121// ["+0&&+0", "is_equal", "recurse_unary", "", 0]
732. 2121
733. 2121 test_cause("recurse_unary");
734. 2121 return is_equal(aa.expression, bb.expression);
735. 2121 }
736. 2121 if (aa.arity === "binary") {
737. 2121
738. 2121// test_cause:
739. 2121// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0]
740. 2121
741. 2121 test_cause("recurse_binary");
742. 2121 return (
743. 2121 aa.id !== "("
744. 2121 && is_equal(aa.expression[0], bb.expression[0])
745. 2121 && is_equal(aa.expression[1], bb.expression[1])
746. 2121 );
747. 2121 }
748. 2121 if (aa.arity === "ternary") {
749. 2121
750. 2121// test_cause:
751. 2121// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0]
752. 2121
753. 2121 test_cause("recurse_ternary");
754. 2121 return (
755. 2121 is_equal(aa.expression[0], bb.expression[0])
756. 2121 && is_equal(aa.expression[1], bb.expression[1])
757. 2121 && is_equal(aa.expression[2], bb.expression[2])
758. 2121 );
759. 2121 }
760. 2121
761. 2121// Probably deadcode.
762. 2121// if (aa.arity === "function" || aa.arity === "regexp") {
763. 2121// return false;
764. 2121// }
765. 2121
766. 2121 jslint_assert(
767. 2121 !(aa.arity === "function" || aa.arity === "regexp"),
768. 2121 `Expected !(aa.arity === "function" || aa.arity === "regexp").`
769. 2121 );
770. 2121
771. 2121// test_cause:
772. 2121// ["undefined&&undefined", "is_equal", "true", "", 0]
773. 2121
774. 2121 test_cause("true");
775. 2121 return true;
776. 6324 }
777. 6324
778. 6324// test_cause:
779. 6324// ["null&&undefined", "is_equal", "false", "", 0]
780. 6324
781. 6324 test_cause("false");
782. 6324 return false;
783. 6324 }
784. 666
785. 28704 function is_weird(thing) {
786. 28704 switch (thing.id) {
787. 1 case "(regexp)":
788. 1 return true;
789. 1 case "=>":
790. 1 return true;
791. 617 case "[":
792. 617 return thing.arity === "unary";
793. 12 case "function":
794. 12 return true;
795. 8 case "{":
796. 8 return true;
797. 28065 default:
798. 28065 return false;
799. 28704 }
800. 28704 }
801. 666
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. 666
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. 666
820. 339116 function test_cause(code, aa, column) {
821. 339116
822. 339116// This function will instrument <cause> to <cause_dict> for test-purposes.
823. 339116
824. 4856 if (option_dict.test_cause) {
825. 4856 cause_dict[JSON.stringify([
826. 4856 String(new Error().stack).replace((
827. 4856 /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm
828. 4856 ), "").match(
829. 4856 /\n at ((?:Object\.\w+?_)?\w+?) /
830. 4856 )[1].replace((
831. 4856 /^Object\./
832. 4856 ), ""),
833. 4856 code,
834. 4856 String(
835. 4856 (aa === undefined || aa === token_global)
836. 4856 ? ""
837. 4856 : aa
838. 4856 ),
839. 4856 column || 0
840. 4856 ])] = true;
841. 4856 }
842. 339116 }
843. 666
844. 1070 function warn(code, the_token, a, b, c, d) {
845. 1070
846. 1070// Same as warn_at, except the warning will be associated with a specific token.
847. 1070// If there is already a warning on this token, suppress the new one. It is
848. 1070// likely that the first warning will be the most meaningful.
849. 1070
850. 1070 let the_warning;
851. 20 the_token = the_token || state.token_nxt;
852. 1070 the_warning = warn_at(
853. 1070 code,
854. 1070 the_token.line,
855. 374 (the_token.from || 0) + jslint_fudge,
856. 830 a || artifact(the_token),
857. 1070 b,
858. 1070 c,
859. 1070 d
860. 1070 );
861. 1070
862. 1070// Issue #408
863. 1070// Warnings that should be ignored sometimes suppress legitimate warnings.
864. 1070
865. 26 if (the_warning.directive_ignore_line) {
866. 26 return the_warning;
867. 1044 }
868. 1044
869. 1044// If there is already a warning on this token, suppress the new one. It is
870. 1044// likely that the first warning will be the most meaningful.
871. 1044
872. 1044 if (the_token.warning) {
873. 192 warning_list.pop();
874. 192 return the_warning;
875. 852 }
876. 852 the_token.warning = the_warning;
877. 852 return the_warning;
878. 852 }
879. 666
880. 1387 function warn_at(code, line, column, a, b, c, d) {
881. 1387
882. 1387// Report an error at some line and column of the program. The warning object
883. 1387// resembles an exception.
884. 1387
885. 1387 let mm;
886. 1387 let warning = Object.assign(empty(), {
887. 1387 a,
888. 1387 b,
889. 1387 c,
890. 1387 code,
891. 1387
892. 1387// Fudge column numbers in warning message.
893. 1387
894. 27 column: column || jslint_fudge,
895. 1387 d,
896. 1387 line,
897. 1387 line_source: "",
898. 1387 name: "JSLintError"
899. 1387 }, line_list[line]);
900. 1387 warning.column = Math.max(
901. 1387 Math.min(warning.column, warning.line_source.length),
902. 1387 jslint_fudge
903. 1387 );
904. 921 test_cause(code, b || a, warning.column);
905. 1387 switch (code) {
906. 1387
907. 1387// The bundle contains the raw text messages that are generated by jslint. It
908. 1387// seems that they are all error messages and warnings. There are no "Atta
909. 1387// boy!" or "You are so awesome!" messages. There is no positive reinforcement
910. 1387// or encouragement. This relentless negativity can undermine self-esteem and
911. 1387// wound the inner child. But if you accept it as sound advice rather than as
912. 1387// personal criticism, it can make your programs better.
913. 1387
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. 284 case "expected_a_b":
948. 284 mm = `Expected '${a}' and instead saw '${b}'.`;
949. 284 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. 1387
995. 1387// PR-378 - Relax warning "function_in_loop".
996. 1387//
997. 1387// case "function_in_loop":
998. 1387// mm = `Don't create functions within a loop.`;
999. 1387// break;
1000. 1387
1001. 1387// PR-390 - Add numeric-separator check.
1002. 1387
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. 1387
1025. 1387// PR-347 - Disable warning "missing_browser".
1026. 1387//
1027. 1387// case "missing_browser":
1028. 1387// mm = `/*global*/ requires the Assume a browser option.`;
1029. 1387// break;
1030. 1387
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. 133 case "undeclared_a":
1089. 133 mm = `Undeclared '${a}'.`;
1090. 133 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. 1387
1110. 1387// PR-347 - Disable warning "unexpected_directive_a".
1111. 1387//
1112. 1387// case "unexpected_directive_a":
1113. 1387// mm = `When using modules, don't use directive '/\u002a${a}'.`;
1114. 1387// break;
1115. 1387
1116. 127 case "unexpected_expression_a":
1117. 127 mm = `Unexpected expression '${a}' in statement position.`;
1118. 127 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. 1387
1161. 1387// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
1162. 1387
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. 1387
1203. 1387// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
1204. 1387
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. 1387 }
1222. 1387
1223. 1387// Validate mm.
1224. 1387
1225. 1387 jslint_assert(mm, code);
1226. 1387 warning.message = mm;
1227. 1387
1228. 1387// PR-242 - Include stack_trace for jslint to debug itself for errors.
1229. 1387
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. 1346 }
1241. 1346 warning_list.push(warning);
1242. 1346 return warning;
1243. 1346 }
1244. 666
1245. 666 try {
1246. 666
1247. 666// tokenize takes a source and produces from it an array of token objects.
1248. 666// JavaScript is notoriously difficult to tokenize because of the horrible
1249. 666// interactions between automatic semicolon insertion, regular expression
1250. 666// literals, and now megastring literals. JSLint benefits from eliminating
1251. 666// automatic semicolon insertion and nested megastring literals, which allows
1252. 666// full tokenization to precede parsing.
1253. 666
1254. 666 option_dict = Object.assign(empty(), option_dict);
1255. 666 Object.assign(state, {
1256. 666 artifact,
1257. 666 catch_list,
1258. 666 catch_stack,
1259. 666 directive_list,
1260. 666 export_dict,
1261. 666 function_list,
1262. 666 function_stack,
1263. 666 global_dict,
1264. 666 global_list,
1265. 666 import_list,
1266. 666 is_equal,
1267. 666 is_weird,
1268. 666 line_list,
1269. 666 mode_json: false, // true if parsing JSON.
1270. 666 mode_module: false, // true if import or export was used.
1271. 666 mode_property: false, // true if directive /*property*/ is
1272. 666 // ... used.
1273. 666 mode_shebang: false, // true if #! is seen on the first line.
1274. 666 option_dict,
1275. 666 property_dict,
1276. 666 source,
1277. 666 stop,
1278. 666 stop_at,
1279. 666 syntax_dict,
1280. 666 tenure,
1281. 666 test_cause,
1282. 666 token_global,
1283. 666 token_list,
1284. 666 token_nxt: token_global,
1285. 666 warn,
1286. 666 warn_at,
1287. 666 warning_list
1288. 666 });
1289. 666
1290. 666// PHASE 1. Split <source> by newlines into <line_list>.
1291. 666
1292. 666 jslint_phase1_split(state);
1293. 666 jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
1294. 666 jslint_assert(
1295. 666 function_stack.length === 0,
1296. 666 `function_stack.length === 0.`
1297. 666 );
1298. 666
1299. 666// PHASE 2. Lex <line_list> into <token_list>.
1300. 666
1301. 666 jslint_phase2_lex(state);
1302. 666 jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
1303. 666 jslint_assert(
1304. 666 function_stack.length === 0,
1305. 666 `function_stack.length === 0.`
1306. 666 );
1307. 666
1308. 666// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
1309. 666
1310. 666 jslint_phase3_parse(state);
1311. 666 jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
1312. 666 jslint_assert(
1313. 666 function_stack.length === 0,
1314. 666 `function_stack.length === 0.`
1315. 666 );
1316. 666
1317. 666// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
1318. 666// recursive traversal. Each node may be processed on the way down
1319. 666// (preaction) and on the way up (postaction).
1320. 666
1321. 516 if (!state.mode_json) {
1322. 516 jslint_phase4_walk(state);
1323. 531 }
1324. 531 jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
1325. 531 jslint_assert(
1326. 531 function_stack.length === 0,
1327. 531 `function_stack.length === 0.`
1328. 531 );
1329. 531
1330. 531// PHASE 5. Check whitespace between tokens in <token_list>.
1331. 531
1332. 531 if (!state.mode_json && warning_list.length === 0) {
1333. 208 jslint_phase5_whitage(state);
1334. 531 }
1335. 531 jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
1336. 531 jslint_assert(
1337. 531 function_stack.length === 0,
1338. 531 `function_stack.length === 0.`
1339. 531 );
1340. 531
1341. 531// PR-347 - Disable warning "missing_browser".
1342. 531//
1343. 531// if (!option_dict.browser) {
1344. 531// directive_list.forEach(function (comment) {
1345. 531// if (comment.directive === "global") {
1346. 531//
1347. 531// // test_cause:
1348. 531// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1]
1349. 531//
1350. 531// warn("missing_browser", comment);
1351. 531// }
1352. 531// });
1353. 531// }
1354. 531
1355. 531 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. 666
1375. 666// Sort warning_list by mode_stop first, line, column respectively.
1376. 666
1377. 976 warning_list.sort(function (aa, bb) {
1378. 976 return (
1379. 976 Boolean(bb.mode_stop) - Boolean(aa.mode_stop)
1380. 886 || aa.line - bb.line
1381. 866 || aa.column - bb.column
1382. 976 );
1383. 976
1384. 976// Update each warning with formatted_message ready-for-use by jslint_cli.
1385. 976
1386. 1157 }).map(function ({
1387. 1157 column,
1388. 1157 line,
1389. 1157 line_source,
1390. 1157 message,
1391. 1157 stack_trace = ""
1392. 1157 }, ii, list) {
1393. 1157 list[ii].formatted_message = String(
1394. 1157 String(ii + 1).padStart(2, " ")
1395. 1157 + ". \u001b[31m" + message + "\u001b[39m"
1396. 1157 + " \u001b[90m\/\/ line " + line + ", column " + column
1397. 1157 + "\u001b[39m\n"
1398. 1157 + (" " + line_source.trim()).slice(0, 72) + "\n"
1399. 1157 + stack_trace
1400. 1157 ).trimRight();
1401. 1157 });
1402. 666
1403. 666 return {
1404. 666 causes: cause_dict,
1405. 666 directives: directive_list,
1406. 666 edition: jslint_edition,
1407. 666 exports: export_dict,
1408. 666 froms: import_list,
1409. 666 functions: function_list,
1410. 666 global: token_global,
1411. 666 id: "(JSLint)",
1412. 666 json: state.mode_json,
1413. 666 lines: line_list,
1414. 666 module: state.mode_module === true,
1415. 164 ok: warning_list.length === 0 && !mode_stop,
1416. 666 option: option_dict,
1417. 666 property: property_dict,
1418. 666 shebang: (
1419. 666 state.mode_shebang
1420. 1 ? line_list[jslint_fudge].line_source
1421. 665 : undefined
1422. 666 ),
1423. 666 stop: mode_stop,
1424. 666 tokens: token_list,
1425. 666 tree: state.token_tree,
1426. 666 warnings: warning_list
1427. 666 };
1428. 666}
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. 13047 ), function (match0) {
1551. 8402 if (whitespace === "" || match0.length < whitespace.length) {
1552. 6592 whitespace = match0;
1553. 6592 }
1554. 13047 return "";
1555. 13047 });
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. 102718function jslint_assert(condition, message) {
1760. 102718
1761. 102718// This function will throw <message> if <condition> is falsy.
1762. 102718
1763. 102716 if (condition) {
1764. 102716 return condition;
1765. 102716 }
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. 23223 while (true) {
1939. 23223 ii = code.indexOf("\n", ii) + 1;
1940. 23223 if (ii === 0) {
1941. 23223 break;
1942. 23223 }
1943. 23223 count += 1;
1944. 23223 }
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. 666function jslint_phase1_split() {
2144. 666
2145. 666// PHASE 1. Split <source> by newlines into <line_list>.
2146. 666
2147. 666 return;
2148. 666}
2149. 1
2150. 666function jslint_phase2_lex(state) {
2151. 666
2152. 666// PHASE 2. Lex <line_list> into <token_list>.
2153. 666
2154. 666 let {
2155. 666 artifact,
2156. 666 directive_list,
2157. 666 global_dict,
2158. 666 global_list,
2159. 666 line_list,
2160. 666 option_dict,
2161. 666 stop,
2162. 666 stop_at,
2163. 666 tenure,
2164. 666 test_cause,
2165. 666 token_global,
2166. 666 token_list,
2167. 666 warn,
2168. 666 warn_at
2169. 666 } = state;
2170. 666 let char; // The current character being lexed.
2171. 666 let column = 0; // The column number of the next character.
2172. 666 let from; // The starting column number of the token.
2173. 666 let from_mega; // The starting column of megastring.
2174. 666 let line = 0; // The line number of the next character.
2175. 666 let line_disable; // The starting line of "/*jslint-disable*/".
2176. 666 let line_mega; // The starting line of megastring.
2177. 666 let line_source = ""; // The remaining line source string.
2178. 666 let line_whole = ""; // The whole line source string.
2179. 666 let mode_digits_empty_string = 1;
2180. 666 let mode_digits_numeric_separator = 2;
2181. 666 let mode_directive = true; // true if directives are still allowed.
2182. 666 let mode_mega = false; // true if currently parsing a megastring
2183. 666 // ... literal.
2184. 666 let mode_regexp; // true if regular expression literal seen on
2185. 666 // ... this line.
2186. 666 let paren_backtrack_list = []; // List of most recent "(" tokens at any
2187. 666 // ... paren-depth.
2188. 666 let paren_depth = 0; // Keeps track of current paren-depth.
2189. 666 let snippet = ""; // A piece of string.
2190. 666 let token_1; // The first token.
2191. 666 let token_prv = token_global; // The previous token including
2192. 666 // ... comments.
2193. 666 let token_prv_expr = token_global; // The previous token excluding
2194. 666 // ... comments.
2195. 666
2196. 666// Most tokens, including the identifiers, operators, and punctuators, can be
2197. 666// found with a regular expression. Regular expressions cannot correctly match
2198. 666// regular expression literals, so we will match those the hard way. String
2199. 666// literals and number literals can be matched by regular expressions, but they
2200. 666// don't provide good warnings. The functions char_after, char_before,
2201. 666// read_digits, and char_after_escape help in the parsing of literals.
2202. 666
2203. 238033 function char_after(match) {
2204. 238033
2205. 238033// Get the next character from the source line. Remove it from the line_source,
2206. 238033// and append it to the snippet. Optionally check that the previous character
2207. 238033// matched an expected value.
2208. 238033
2209. 5980 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. 238023 }
2221. 238023 char = line_source.slice(0, 1);
2222. 238023 line_source = line_source.slice(1);
2223. 238023 snippet += char || " ";
2224. 238033 column += 1;
2225. 238033 return char;
2226. 238033 }
2227. 666
2228. 2951 function char_after_escape(extra) {
2229. 2951
2230. 2951// Validate char after escape "\\".
2231. 2951
2232. 2951 char_after("\\");
2233. 2951 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. 1013 case "n":
2251. 1013 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. 2951 }
2305. 2951 }
2306. 666
2307. 10019 function char_before() {
2308. 10019
2309. 10019// Back up one character by moving a character from the end of the snippet to
2310. 10019// the front of the line_source.
2311. 10019
2312. 10019 char = snippet.slice(-1);
2313. 10019 line_source = char + line_source;
2314. 10019 column -= char.length;
2315. 10019
2316. 10019// Remove last character from snippet.
2317. 10019
2318. 10019 snippet = snippet.slice(0, -1);
2319. 10019 return char;
2320. 10019 }
2321. 666
2322. 8930 function check_numeric_separator(digits, column) {
2323. 8930
2324. 8930// This function will check for illegal numeric-separator in <digits>.
2325. 8930
2326. 8930 digits.replace((
2327. 8930 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. 8930 }
2341. 666
2342. 11181 function lex_comment() {
2343. 11181 let body;
2344. 11181 let ii = 0;
2345. 11181 let jj = 0;
2346. 11181 let the_comment;
2347. 11181
2348. 11181// Create a comment object. Comments are not allowed in JSON text. Comments can
2349. 11181// include directives and notices of incompletion.
2350. 11181
2351. 11181// Create token from comment //....
2352. 11181
2353. 11067 if (snippet === "//") {
2354. 11067 snippet = line_source;
2355. 11067 line_source = "";
2356. 11067 the_comment = token_create("(comment)", snippet);
2357. 11067 if (mode_mega) {
2358. 11067
2359. 11067// test_cause:
2360. 11067// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
2361. 11067
2362. 11067 warn("unexpected_comment", the_comment, "`");
2363. 11067 }
2364. 11067
2365. 11067// Create token from comment /*...*/.
2366. 11067
2367. 11067 } 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. 11178 }
2420. 11178
2421. 11178// Uncompleted work comment.
2422. 11178
2423. 11178 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. 11178 }
2430. 11178
2431. 11178// Lex directives in comment.
2432. 11178
2433. 11178 [
2434. 11178 the_comment.directive, body
2435. 11178 ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
2436. 11101 if (the_comment.directive === undefined) {
2437. 11101 return the_comment;
2438. 11101 }
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. 1830 body.replace(jslint_rgx_directive_part, function (
2458. 1830 match0,
2459. 1830 key,
2460. 1830 val,
2461. 1830 jj
2462. 1830 ) {
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. 1829 }
2470. 1829 if (match0 === "") {
2471. 76 return "";
2472. 1754 }
2473. 1754 ii += match0.length;
2474. 1754 switch (the_comment.directive) {
2475. 1754 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. 1646 case "property":
2500. 1646 state.mode_property = true;
2501. 1646 tenure[key] = true;
2502. 1646 break;
2503. 1754 }
2504. 1754 return "";
2505. 1754 });
2506. 76 return the_comment;
2507. 76 }
2508. 666
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. 666
2612. 8857 function lex_number() {
2613. 8857 let prefix = snippet;
2614. 8857
2615. 8857// PR-390 - Add numeric-separator check.
2616. 8857
2617. 8857 check_numeric_separator(prefix, column - prefix.length);
2618. 8857 char_after();
2619. 2886 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. 8824 default:
2632. 8824 if (char === ".") {
2633. 8824 read_digits("d", mode_digits_numeric_separator);
2634. 8824 }
2635. 8824 if (char === "E" || char === "e") {
2636. 8824 char_after(char);
2637. 8824 if (char !== "+" && char !== "-") {
2638. 8824 char_before();
2639. 8824 }
2640. 8824 read_digits("d", mode_digits_numeric_separator);
2641. 8824 }
2642. 8857 }
2643. 8857
2644. 8857// If the next character after a number is a digit or letter, then something
2645. 8857// unexpected is going on.
2646. 8857
2647. 8857 if (
2648. 1573 (char >= "0" && char <= "9")
2649. 44 || (char >= "a" && char <= "z")
2650. 8856 || (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. 8856 }
2664. 8856 char_before();
2665. 8856 return token_create("(number)", snippet);
2666. 8856 }
2667. 666
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. 793 function lex_regexp_group() {
2764. 793
2765. 793// RegExp
2766. 793// Lex sequence of characters in regexp.
2767. 793
2768. 793 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. 793 }
2785. 5584 while (true) {
2786. 5584 switch (char) {
2787. 5584 case "":
2788. 5584 case ")":
2789. 5584 case "/":
2790. 5584 case "]":
2791. 5584 return;
2792. 5584
2793. 5584// PR-362 - Relax regexp-warning against using <space>.
2794. 5584//
2795. 5584// case " ":
2796. 5584//
2797. 5584// // test_cause:
2798. 5584// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
2799. 5584//
2800. 5584// warn_at("expected_a_b", line, column, "\\s", " ");
2801. 5584// char_after();
2802. 5584// break;
2803. 5584
2804. 5584 case "$":
2805. 5584 if (line_source[0] !== "/") {
2806. 5584 mode_regexp_multiline = true;
2807. 5584 }
2808. 5584 char_after();
2809. 5584 break;
2810. 5584 case "(":
2811. 5584
2812. 5584// RegExp
2813. 5584// Match a group that starts with left paren.
2814. 5584
2815. 5584 char_after("(");
2816. 5584 switch (char) {
2817. 5584 case ":":
2818. 5584
2819. 5584// test_cause:
2820. 5584// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
2821. 5584// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
2822. 5584
2823. 5584 warn_at("expected_a_before_b", line, column, "?", ":");
2824. 5584 break;
2825. 5584 case "?":
2826. 5584 char_after("?");
2827. 5584 switch (char) {
2828. 5584 case "!":
2829. 5584
2830. 5584// PR-437 - Add grammar for regexp-named-capture-group.
2831. 5584
2832. 5584 case "<":
2833. 5584 case "=":
2834. 5584 char_after();
2835. 5584 break;
2836. 5584 default:
2837. 5584 char_after(":");
2838. 5584 }
2839. 5584 break;
2840. 5584 }
2841. 5584
2842. 5584// RegExp
2843. 5584// Recurse lex_regexp_group().
2844. 5584
2845. 5584 lex_regexp_group();
2846. 5584 char_after(")");
2847. 5584 break;
2848. 5584 case "*":
2849. 5584 case "+":
2850. 5584 case "?":
2851. 5584 case "{":
2852. 5584 case "}":
2853. 5584
2854. 5584// test_cause:
2855. 5584// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
2856. 5584// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
2857. 5584// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
2858. 5584// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
2859. 5584// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
2860. 5584
2861. 5584 warn_at("expected_a_before_b", line, column, "\\", char);
2862. 5584 char_after();
2863. 5584 break;
2864. 5584 case "[":
2865. 5584 lex_regexp_bracketed();
2866. 5584 break;
2867. 5584 case "\\":
2868. 5584
2869. 5584// test_cause:
2870. 5584// ["aa=/\\/", "lex_regexp_group", "escape", "", 0]
2871. 5584
2872. 5584 test_cause("escape");
2873. 5584
2874. 5584// PR-437 - Add grammar for regexp-named-backreference.
2875. 5584
2876. 5584 char_after_escape("BbDdSsWw^${}[]():=!.|*+?k");
2877. 5584 break;
2878. 5584 case "^":
2879. 5584 if (snippet !== "^") {
2880. 5584 mode_regexp_multiline = true;
2881. 5584 }
2882. 5584 char_after();
2883. 5584 break;
2884. 5584 case "`":
2885. 5584 if (mode_mega) {
2886. 5584
2887. 5584// test_cause:
2888. 5584// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
2889. 5584
2890. 5584 warn_at("unexpected_a", line, column, "`");
2891. 5584 }
2892. 5584 char_after();
2893. 5584 break;
2894. 5584 default:
2895. 5584 char_after();
2896. 5584 }
2897. 5584
2898. 5584// RegExp
2899. 5584// Match an optional quantifier.
2900. 5584
2901. 5584 switch (char) {
2902. 5584 case "*":
2903. 5584 case "+":
2904. 5584 if (char_after(char) === "?") {
2905. 5584
2906. 5584// test_cause:
2907. 5584// ["aa=/.*?/", "lex_regexp_group", "?", "", 0]
2908. 5584// ["aa=/.+?/", "lex_regexp_group", "?", "", 0]
2909. 5584
2910. 5584 test_cause("?");
2911. 5584 char_after("?");
2912. 5584 }
2913. 5584 break;
2914. 5584 case "?":
2915. 5584 if (char_after("?") === "?") {
2916. 5584
2917. 5584// test_cause:
2918. 5584// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
2919. 5584
2920. 5584 warn_at("unexpected_a", line, column, char);
2921. 5584 char_after("?");
2922. 5584 }
2923. 5584 break;
2924. 5584 case "{":
2925. 5584 if (read_digits("d", mode_digits_empty_string) === 0) {
2926. 5584
2927. 5584// test_cause:
2928. 5584// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
2929. 5584
2930. 5584 warn_at("expected_a_before_b", line, column, "0", ",");
2931. 5584 }
2932. 5584 if (char === ",") {
2933. 5584
2934. 5584// test_cause:
2935. 5584// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
2936. 5584
2937. 5584 test_cause("comma");
2938. 5584 read_digits("d", mode_digits_empty_string);
2939. 5584 }
2940. 5584 if (char_after("}") === "?") {
2941. 5584
2942. 5584// test_cause:
2943. 5584// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
2944. 5584
2945. 5584 warn_at("unexpected_a", line, column, char);
2946. 5584 char_after("?");
2947. 5584 }
2948. 5584 break;
2949. 5584 }
2950. 5584 }
2951. 793 }
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. 511 (char >= "a" && char <= "z\uffff")
2989. 573 || (char >= "A" && char <= "Z\uffff")
2990. 509 ) {
2991. 509
2992. 509// RegExp
2993. 509// Process dangling flag letters.
2994. 509
2995. 509 switch (!flag[char] && char) {
2996. 509 case "g":
2997. 509 break;
2998. 509 case "i":
2999. 509 break;
3000. 509 case "m":
3001. 509 break;
3002. 509 case "u":
3003. 509 break;
3004. 509 case "y":
3005. 509
3006. 509// test_cause:
3007. 509// ["aa=/./gimuy", "lex_regexp", "flag", "", 0]
3008. 509
3009. 509 test_cause("flag");
3010. 509 break;
3011. 509 default:
3012. 509
3013. 509// test_cause:
3014. 509// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
3015. 509// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
3016. 509
3017. 509 warn_at("unexpected_a", line, column, char);
3018. 509 }
3019. 509 flag[char] = true;
3020. 509 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. 666
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. 666
3148. 24308 function lex_string(quote) {
3149. 24308
3150. 24308// Create a string token.
3151. 24308
3152. 24308 let the_token;
3153. 24306 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. 24308 snippet = "";
3161. 24308 char_after();
3162. 24308
3163. 24308// Lex/loop through each character in "...".
3164. 24308
3165. 216882 while (true) {
3166. 216882 switch (char) {
3167. 216882 case "":
3168. 216882
3169. 216882// test_cause:
3170. 216882// ["\"", "lex_string", "unclosed_string", "", 1]
3171. 216882
3172. 216882 return stop_at("unclosed_string", line, column);
3173. 216882 case "\\":
3174. 216882 char_after_escape(quote);
3175. 216882 break;
3176. 216882 case "`":
3177. 216882 if (mode_mega) {
3178. 216882
3179. 216882// test_cause:
3180. 216882// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
3181. 216882
3182. 216882 warn_at("unexpected_a", line, column, "`");
3183. 216882 }
3184. 216882 char_after("`");
3185. 216882 break;
3186. 216882 case quote:
3187. 216882
3188. 216882// Remove last character from snippet.
3189. 216882
3190. 216882 snippet = snippet.slice(0, -1);
3191. 216882 the_token = token_create("(string)", snippet);
3192. 216882 the_token.quote = quote;
3193. 216882 return the_token;
3194. 216882 default:
3195. 216882 char_after();
3196. 216882 }
3197. 216882 }
3198. 24308 }
3199. 666
3200. 251943 function lex_token() {
3201. 251943 let match;
3202. 251943
3203. 251943// Lex/loop through each whitespace.
3204. 251943
3205. 375587 while (true) {
3206. 375587
3207. 375587// Lex/loop through each blank-line.
3208. 375587
3209. 375587 while (!line_source) {
3210. 375587 line_source = read_line();
3211. 375587 from = 0;
3212. 375587 if (line_source === undefined) {
3213. 375587 return (
3214. 375587 mode_mega
3215. 375587
3216. 375587// test_cause:
3217. 375587// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
3218. 375587
3219. 375587 ? stop_at("unclosed_mega", line_mega, from_mega)
3220. 375587 : line_disable !== undefined
3221. 375587
3222. 375587// test_cause:
3223. 375587// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
3224. 375587
3225. 375587 ? stop_at("unclosed_disable", line_disable)
3226. 375587 : token_create("(end)")
3227. 375587 );
3228. 375587 }
3229. 375587 }
3230. 375587 from = column;
3231. 375587 match = line_source.match(jslint_rgx_token);
3232. 375587
3233. 375587// match[1] token
3234. 375587// match[2] whitespace
3235. 375587// match[3] identifier
3236. 375587// match[4] number
3237. 375587// match[5] rest
3238. 375587
3239. 375587 if (!match) {
3240. 375587
3241. 375587// test_cause:
3242. 375587// ["#", "lex_token", "unexpected_char_a", "#", 1]
3243. 375587
3244. 375587 return stop_at(
3245. 375587 "unexpected_char_a",
3246. 375587 line,
3247. 375587 column,
3248. 375587 line_source[0]
3249. 375587 );
3250. 375587 }
3251. 375587 snippet = match[1];
3252. 375587 column += snippet.length;
3253. 375587 line_source = match[5];
3254. 375587 if (!match[2]) {
3255. 375587 break;
3256. 375587 }
3257. 375587 }
3258. 251302
3259. 251302// The token is an identifier.
3260. 251302
3261. 251302 if (match[3]) {
3262. 67598 return token_create(snippet, undefined, true);
3263. 183704 }
3264. 183704
3265. 183704// Create token from number.
3266. 183704
3267. 183704 if (match[4]) {
3268. 8857 return lex_number();
3269. 174847 }
3270. 174847
3271. 174847// Create token from string "..." or '...'.
3272. 174847
3273. 174847 if (snippet === "\"" || snippet === "'") {
3274. 24308 return lex_string(snippet);
3275. 150539 }
3276. 150539
3277. 150539// Create token from megastring `...`.
3278. 150539
3279. 150539 if (snippet === "`") {
3280. 789 return lex_megastring();
3281. 149750 }
3282. 149750
3283. 149750// Create token from comment /*...*/ or //....
3284. 149750
3285. 149750 if (snippet === "/*" || snippet === "//") {
3286. 11181 return lex_comment();
3287. 138569 }
3288. 138569
3289. 138569// Create token from slash /.
3290. 138569
3291. 138569 if (snippet === "/") {
3292. 598 return lex_slash_or_regexp();
3293. 137971 }
3294. 137971 return token_create(snippet);
3295. 137971 }
3296. 666
3297. 2659 function option_set_item(key, val) {
3298. 2659
3299. 2659// These are the options that are recognized in the option object or that may
3300. 2659// appear in a /*jslint*/ directive. Most options will have a boolean value,
3301. 2659// usually true. Some options will also predefine some number of global
3302. 2659// variables.
3303. 2659
3304. 2659 switch (key) {
3305. 657 case "beta": // Enable experimental warnings.
3306. 659 case "bitwise": // Allow bitwise operator.
3307. 666 case "browser": // Assume browser environment.
3308. 668 case "convert": // Allow conversion operator.
3309. 670 case "couch": // Assume CouchDb environment.
3310. 676 case "devel": // Allow console.log() and friends.
3311. 2008 case "ecma": // Assume ECMAScript environment.
3312. 2014 case "eval": // Allow eval().
3313. 2018 case "fart": // Allow complex fat-arrow.
3314. 2023 case "for": // Allow for-statement.
3315. 2029 case "getset": // Allow get() and set().
3316. 2034 case "indent2": // Use 2-space indent.
3317. 2036 case "long": // Allow long lines.
3318. 2083 case "node": // Assume Node.js environment.
3319. 2087 case "nomen": // Allow weird property name.
3320. 2089 case "single": // Allow single-quote strings.
3321. 2091 case "subscript": // Allow identifier in subscript-notation.
3322. 2594 case "test_cause": // Test jslint's causes.
3323. 2596 case "test_internal_error": // Test jslint's internal-error
3324. 2659 // ... handling-ability.
3325. 2598 case "this": // Allow 'this'.
3326. 2601 case "trace": // Include jslint stack-trace in warnings.
3327. 2605 case "unordered": // Allow unordered cases, params, properties,
3328. 2659 // ... variables, and exports.
3329. 2609 case "variable": // Allow unordered const and let declarations
3330. 2659 // ... that are not at top of function-scope.
3331. 2611 case "white": // Allow messy whitespace.
3332. 2611 option_dict[key] = val;
3333. 2611 break;
3334. 2659
3335. 2659// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
3336. 2659
3337. 2 case "evil":
3338. 2 return option_set_item("eval", val);
3339. 2659
3340. 2659// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
3341. 2659
3342. 2 case "name":
3343. 2 return option_set_item("nomen", val);
3344. 44 default:
3345. 44 return false;
3346. 2611 }
3347. 2611
3348. 2611// Initialize global-variables.
3349. 2611
3350. 2611 switch (val && key) {
3351. 2659
3352. 2659// Assign global browser variables to global_dict.
3353. 2659/*
3354. 2659// /\*jslint beta, browser, devel*\/
3355. 2659console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4));
3356. 2659*/
3357. 2659
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. 2659
3447. 2659// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
3448. 2659
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. 2659
3470. 2659// These are the globals that are provided by the language standard.
3471. 2659// Assign global ECMAScript variables to global_dict.
3472. 2659/*
3473. 2659node --input-type=module --eval '
3474. 2659// /\*jslint beta, node*\/
3475. 2659import https from "https";
3476. 2659(async function () {
3477. 2659 let dict = {import: true};
3478. 2659 let result = "";
3479. 2659 await new Promise(function (resolve) {
3480. 2659 https.get((
3481. 2659 "https://raw.githubusercontent.com/mdn/content/main/files"
3482. 2659 + "/en-us/web/javascript/reference/global_objects/index.md"
3483. 2659 ), function (res) {
3484. 2659 res.on("data", function (chunk) {
3485. 2659 result += chunk;
3486. 2659 }).on("end", resolve).setEncoding("utf8");
3487. 2659 });
3488. 2659 });
3489. 2659 result.replace((
3490. 2659 /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g
3491. 2659 ), function (ignore, key) {
3492. 2659 if (globalThis.hasOwnProperty(key)) {
3493. 2659 dict[key] = true;
3494. 2659 }
3495. 2659 return "";
3496. 2659 });
3497. 2659 console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
3498. 2659}());
3499. 2659'
3500. 2659*/
3501. 2659
3502. 1332 case "ecma":
3503. 1332 object_assign_from_list(global_dict, [
3504. 1332 "AggregateError",
3505. 1332 "Array",
3506. 1332 "ArrayBuffer",
3507. 1332 "Atomics",
3508. 1332 "BigInt",
3509. 1332 "BigInt64Array",
3510. 1332 "BigUint64Array",
3511. 1332 "Boolean",
3512. 1332 "DataView",
3513. 1332 "Date",
3514. 1332 "Error",
3515. 1332 "EvalError",
3516. 1332 "Float32Array",
3517. 1332 "Float64Array",
3518. 1332 "Function",
3519. 1332 "Infinity",
3520. 1332 "Int16Array",
3521. 1332 "Int32Array",
3522. 1332 "Int8Array",
3523. 1332 "Intl",
3524. 1332 "JSON",
3525. 1332 "Map",
3526. 1332 "Math",
3527. 1332 "NaN",
3528. 1332 "Number",
3529. 1332 "Object",
3530. 1332 "Promise",
3531. 1332 "Proxy",
3532. 1332 "RangeError",
3533. 1332 "ReferenceError",
3534. 1332 "Reflect",
3535. 1332 "RegExp",
3536. 1332 "Set",
3537. 1332 "SharedArrayBuffer",
3538. 1332 "String",
3539. 1332 "Symbol",
3540. 1332 "SyntaxError",
3541. 1332 "TypeError",
3542. 1332 "URIError",
3543. 1332 "Uint16Array",
3544. 1332 "Uint32Array",
3545. 1332 "Uint8Array",
3546. 1332 "Uint8ClampedArray",
3547. 1332 "WeakMap",
3548. 1332 "WeakSet",
3549. 1332 "WebAssembly",
3550. 1332 "decodeURI",
3551. 1332 "decodeURIComponent",
3552. 1332 "encodeURI",
3553. 1332 "encodeURIComponent",
3554. 1332 "eval",
3555. 1332 "globalThis",
3556. 1332 "import",
3557. 1332 "isFinite",
3558. 1332 "isNaN",
3559. 1332 "parseFloat",
3560. 1332 "parseInt",
3561. 1332 "undefined"
3562. 1332 ], "ECMAScript");
3563. 1332 break;
3564. 2659
3565. 2659// Assign global Node.js variables to global_dict.
3566. 2659/*
3567. 2659node --input-type=module --eval '
3568. 2659// /\*jslint beta, node*\/
3569. 2659import moduleHttps from "https";
3570. 2659(async function () {
3571. 2659 let dict = Object.create(null);
3572. 2659 let result = "";
3573. 2659 await new Promise(function (resolve) {
3574. 2659 moduleHttps.get((
3575. 2659 "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api"
3576. 2659 + "/globals.md"
3577. 2659 ), function (res) {
3578. 2659 res.on("data", function (chunk) {
3579. 2659 result += chunk;
3580. 2659 }).on("end", resolve).setEncoding("utf8");
3581. 2659 });
3582. 2659 });
3583. 2659 result.replace((
3584. 2659 /\n(?:\* \[`|## |## Class: )`\w+/g
3585. 2659 ), function (match0) {
3586. 2659 dict[match0.split("`")[1]] = true;
3587. 2659 return "";
3588. 2659 });
3589. 2659 console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
3590. 2659}());
3591. 2659'
3592. 2659*/
3593. 2659
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. 2611 }
3636. 2611 return true;
3637. 2611 }
3638. 666
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. 666
3683. 105181 function read_line() {
3684. 105181
3685. 105181// Put the next line of source in line_source. If the line contains tabs,
3686. 105181// replace them with spaces and give a warning. Also warn if the line contains
3687. 105181// unsafe characters or is too damn long.
3688. 105181
3689. 105181 if (
3690. 105181 !option_dict.long
3691. 105177 && 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. 105181 column = 0;
3704. 105181 line += 1;
3705. 105181 mode_regexp = false;
3706. 105181 line_source = undefined;
3707. 105181 line_whole = "";
3708. 643 if (line_list[line] === undefined) {
3709. 643 return line_source;
3710. 104538 }
3711. 104538 line_source = line_list[line].line_source;
3712. 104538 line_whole = line_source;
3713. 104538
3714. 104538// Scan each line for following ignore-directives:
3715. 104538// "/*jslint-disable*/"
3716. 104538// "/*jslint-enable*/"
3717. 104538// "//jslint-ignore-line"
3718. 104538
3719. 104538 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. 104533 } else if (line_source === "/*jslint-enable*/") {
3727. 104533 if (line_disable === undefined) {
3728. 104533
3729. 104533// test_cause:
3730. 104533// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
3731. 104533
3732. 104533 stop_at("unopened_enable", line);
3733. 104533 }
3734. 104533 line_disable = undefined;
3735. 104533 } else if (
3736. 104533 line_source.endsWith(" //jslint-ignore-line")
3737. 104533 || line_source.endsWith(" //jslint-quiet")
3738. 104533 ) {
3739. 104533
3740. 104533// test_cause:
3741. 104533// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0]
3742. 104533
3743. 104533 test_cause("jslint_ignore_line");
3744. 104533 line_list[line].directive_ignore_line = true;
3745. 104537 }
3746. 104537 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. 104537 }
3754. 104537 // jslint_rgx_tab
3755. 104537 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. 104537 }
3765. 104537 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. 104537 }
3772. 104537 return line_source;
3773. 104537 }
3774. 666
3775. 254929 function token_create(id, value, identifier) {
3776. 254929
3777. 254929// Create the token object and append it to token_list.
3778. 254929
3779. 254929 let the_token = {
3780. 254929 from,
3781. 254929 id,
3782. 254929 identifier: Boolean(identifier),
3783. 254929 line,
3784. 254929 nr: token_list.length,
3785. 254929 thru: column,
3786. 254929 value
3787. 254929 };
3788. 254929 token_list.push(the_token);
3789. 254929
3790. 254929// Directives must appear before the first statement.
3791. 254929
3792. 243751 if (id !== "(comment)" && id !== ";") {
3793. 227751 mode_directive = false;
3794. 227751 }
3795. 254929
3796. 254929// If this token is an identifier that touches a preceding number, or
3797. 254929// a "/", comment, or regular expression literal that touches a preceding
3798. 254929// comment or regular expression literal, then give a missing space warning.
3799. 254929// This warning is not suppressed by option_dict.white.
3800. 254929
3801. 254929 if (
3802. 254929 token_prv.line === line
3803. 185625 && token_prv.thru === from
3804. 119780 && (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. 11580 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. 11580 if (token_prv_expr.id === "." && the_token.identifier) {
3826. 11575 the_token.dot = true;
3827. 11575 }
3828. 254929
3829. 254929// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
3830. 254929// Farts are now detected by keeping a list of most recent "(" tokens at any
3831. 254929// given depth. When a "=>" token is encountered, the most recent "(" token at
3832. 254929// current depth is marked as a fart.
3833. 254929
3834. 254929 switch (id) {
3835. 17561 case "(":
3836. 17561 paren_backtrack_list[paren_depth] = the_token;
3837. 17561 paren_depth += 1;
3838. 17561 break;
3839. 17560 case ")":
3840. 17560 paren_depth -= 1;
3841. 17560 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. 254929 }
3851. 254929
3852. 254929// The previous token is used to detect adjacency problems.
3853. 254929
3854. 254929 token_prv = the_token;
3855. 254929
3856. 254929// The token_prv_expr token is a previous token that was not a comment.
3857. 254929// The token_prv_expr token
3858. 254929// is used to disambiguate "/", which can mean division or regular expression
3859. 254929// literal.
3860. 254929
3861. 243751 if (token_prv.id !== "(comment)") {
3862. 243751 token_prv_expr = token_prv;
3863. 243751 }
3864. 254929 return the_token;
3865. 254929 }
3866. 666
3867. 666// Init global_dict and option_dict.
3868. 666
3869. 666 option_set_item("ecma", true);
3870. 1890 Object.keys(option_dict).sort().forEach(function (key) {
3871. 1890 option_set_item(key, option_dict[key] === true);
3872. 1890 });
3873. 666 object_assign_from_list(global_dict, global_list, "user-defined");
3874. 666
3875. 666// Scan first line for "#!" and ignore it.
3876. 666
3877. 1 if (line_list[jslint_fudge].line_source.startsWith("#!")) {
3878. 1 line += 1;
3879. 1 state.mode_shebang = true;
3880. 1 }
3881. 666 token_1 = lex_token();
3882. 638 state.mode_json = token_1.id === "{" || token_1.id === "[";
3883. 666
3884. 666// Lex/loop through each token until (end).
3885. 666
3886. 249164 while (true) {
3887. 249164 if (lex_token().id === "(end)") {
3888. 249164 break;
3889. 249164 }
3890. 249164 }
3891. 666}
3892. 1
3893. 629function jslint_phase3_parse(state) {
3894. 629
3895. 629// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
3896. 629
3897. 629// Parsing:
3898. 629
3899. 629// Parsing weaves the tokens into an abstract syntax tree. During that process,
3900. 629// a token may be given any of these properties:
3901. 629
3902. 629// arity string
3903. 629// label identifier
3904. 629// name identifier
3905. 629// expression expressions
3906. 629// block statements
3907. 629// else statements (else, default, catch)
3908. 629
3909. 629// Specialized tokens may have additional properties.
3910. 629
3911. 629 let anon = "anonymous"; // The guessed name for anonymous functions.
3912. 629 let {
3913. 629 artifact,
3914. 629 catch_list,
3915. 629 catch_stack,
3916. 629 export_dict,
3917. 629 function_list,
3918. 629 function_stack,
3919. 629 global_dict,
3920. 629 import_list,
3921. 629 is_equal,
3922. 629 option_dict,
3923. 629 property_dict,
3924. 629 stop,
3925. 629 syntax_dict,
3926. 629 tenure,
3927. 629 test_cause,
3928. 629 token_global,
3929. 629 token_list,
3930. 629 warn,
3931. 629 warn_at
3932. 629 } = state;
3933. 629 let catchage = catch_stack[0]; // The current catch-block.
3934. 629 let functionage = token_global; // The current function.
3935. 629 let mode_var; // "var" if using var; "let" if using let.
3936. 629 let token_ii = 0; // The number of the next token.
3937. 629 let token_now = token_global; // The current token being examined in
3938. 629 // ... the parse.
3939. 629 let token_nxt = token_global; // The next token to be examined in
3940. 629 // ... <token_list>.
3941. 629
3942. 244087 function advance(id, match) {
3943. 244087
3944. 244087// Produce the next token.
3945. 244087
3946. 244087// Attempt to give helpful names to anonymous functions.
3947. 244087
3948. 244087 if (
3949. 244087 token_now.identifier
3950. 67555 && token_now.id !== "function"
3951. 65563 && token_now.id !== "async"
3952. 65382 ) {
3953. 65382 anon = token_now.id;
3954. 178705 } else if (
3955. 178705 token_now.id === "(string)"
3956. 178705 && jslint_rgx_identifier.test(token_now.value)
3957. 178705 ) {
3958. 178705 anon = token_now.value;
3959. 178705 }
3960. 244087
3961. 244087// Attempt to match token_nxt with an expected id.
3962. 244087
3963. 119971 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. 244061 }
3985. 244061
3986. 244061// Promote the tokens, skipping comments.
3987. 244061
3988. 244061 token_now = token_nxt;
3989. 255236 while (true) {
3990. 255236 token_nxt = token_list[token_ii];
3991. 255236 state.token_nxt = token_nxt;
3992. 255236 token_ii += 1;
3993. 255236 if (token_nxt.id !== "(comment)") {
3994. 255236 if (token_nxt.id === "(end)") {
3995. 255236 token_ii -= 1;
3996. 255236 }
3997. 255236 break;
3998. 255236 }
3999. 255236 if (state.mode_json) {
4000. 255236
4001. 255236// test_cause:
4002. 255236// ["[//]", "advance", "unexpected_a", "(comment)", 2]
4003. 255236
4004. 255236 warn("unexpected_a");
4005. 255236 }
4006. 255236 }
4007. 244087 }
4008. 629
4009. 7548 function assignment(id) {
4010. 7548
4011. 7548// Create an assignment operator. The one true assignment is different because
4012. 7548// its left side, when it is a variable, is not treated as an expression.
4013. 7548// That case is special because that is when a variable gets initialized. The
4014. 7548// other assignment operators can modify, but they cannot initialize.
4015. 7548
4016. 7548 const the_symbol = symbol(id, 20);
4017. 5012 the_symbol.led_infix = function (left) {
4018. 5012 const the_token = token_now;
4019. 5012 let right;
4020. 5012 the_token.arity = "assignment";
4021. 5012 right = parse_expression(20 - 1);
4022. 4233 if (id === "=" && left.arity === "variable") {
4023. 2817 the_token.names = left;
4024. 2817 the_token.expression = right;
4025. 2817 } else {
4026. 2191 the_token.expression = [left, right];
4027. 5008 }
4028. 5008 if (
4029. 5008 right.arity === "assignment"
4030. 5008 || right.arity === "preassign"
4031. 5006 || right.arity === "postassign"
4032. 2 ) {
4033. 2 warn("unexpected_a", right);
4034. 5008 }
4035. 5008 check_mutation(left);
4036. 5008 return the_token;
4037. 5008 };
4038. 7548 return the_symbol;
4039. 7548 }
4040. 629
4041. 5702 function block(special) {
4042. 5702
4043. 5702// Parse a block, a sequence of statements wrapped in braces.
4044. 5702// special "body" The block is a function body.
4045. 5702// "ignore" No warning on an empty block.
4046. 5702// "naked" No advance.
4047. 5702// undefined An ordinary block.
4048. 5702
4049. 5702 let stmts;
4050. 5702 let the_block;
4051. 5697 if (special !== "naked") {
4052. 5697 advance("{");
4053. 5701 }
4054. 5701 the_block = token_now;
4055. 5701 if (special !== "body") {
4056. 3704 functionage.statement_prv = the_block;
4057. 5701 }
4058. 5701 the_block.arity = "statement";
4059. 5701 the_block.body = special === "body";
4060. 5701
4061. 5701// Top level function bodies may include the "use strict" pragma.
4062. 5701
4063. 5701 if (
4064. 5701 special === "body"
4065. 5701 && 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. 5701 }
4072. 5701 stmts = parse_statements();
4073. 5701 the_block.block = stmts;
4074. 5701 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. 5622 } else {
4084. 5622 the_block.disrupt = stmts[stmts.length - 1].disrupt;
4085. 5694 }
4086. 5694 advance("}");
4087. 5694 return the_block;
4088. 5694 }
4089. 629
4090. 23258 function check_left(left, right) {
4091. 23258
4092. 23258// Warn if the left is not one of these:
4093. 23258// ?.
4094. 23258// ?:
4095. 23258// e()
4096. 23258// e.b
4097. 23258// e[b]
4098. 23258// identifier
4099. 23258
4100. 23258 const id = left.id;
4101. 23258 if (
4102. 23258 !left.identifier
4103. 6285 && (
4104. 6285 left.arity !== "ternary"
4105. 6285 || (
4106. 6285 !check_left(left.expression[1])
4107. 6285 && !check_left(left.expression[2])
4108. 6285 )
4109. 6285 )
4110. 6285 && (
4111. 6285 left.arity !== "binary"
4112. 6285 || (id !== "." && id !== "?." && id !== "(" && id !== "[")
4113. 6285 )
4114. 11 ) {
4115. 11 warn("unexpected_a", right || token_nxt);
4116. 11 return false;
4117. 23247 }
4118. 23247 return true;
4119. 23247 }
4120. 629
4121. 5011 function check_mutation(the_thing) {
4122. 5011
4123. 5011// The only expressions that may be assigned to are
4124. 5011// e.b
4125. 5011// e[b]
4126. 5011// v
4127. 5011// [destructure]
4128. 5011// {destructure}
4129. 5011
4130. 5011 if (
4131. 5011 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. 5004 }
4143. 5004 return true;
4144. 5004 }
4145. 629
4146. 2203 function check_not_top_level(thing) {
4147. 2203
4148. 2203// Some features should not be at the outermost level.
4149. 2203
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. 2203 }
4160. 629
4161. 3358 function check_ordered(type, token_list) {
4162. 3358
4163. 3358// This function will warn if <token_list> is unordered.
4164. 3358
4165. 4056 token_list.reduce(function (aa, token) {
4166. 4056 const bb = artifact(token);
4167. 4042 if (!option_dict.unordered && aa > bb) {
4168. 4 warn("expected_a_b_before_c_d", token, type, bb, type, aa);
4169. 4 }
4170. 4056 return bb;
4171. 4056 }, "");
4172. 3358 }
4173. 629
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. 629
4228. 3252 function condition() {
4229. 3252
4230. 3252// Parse the condition part of a do, if, while.
4231. 3252
4232. 3252 const the_paren = token_nxt;
4233. 3252 let the_value;
4234. 3252
4235. 3252// test_cause:
4236. 3252// ["do{}while()", "condition", "", "", 0]
4237. 3252// ["if(){}", "condition", "", "", 0]
4238. 3252// ["while(){}", "condition", "", "", 0]
4239. 3252
4240. 3252 test_cause("");
4241. 3252 the_paren.free = true;
4242. 3252 advance("(");
4243. 3252 the_value = parse_expression(0);
4244. 3252 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. 3248 }
4252. 3248
4253. 3248// Check for anticondition.
4254. 3248
4255. 3248 switch (the_value.id) {
4256. 3248 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. 3248 }
4324. 3248 return the_value;
4325. 3248 }
4326. 629
4327. 10064 function constant(id, type, value) {
4328. 10064
4329. 10064// Create a constant symbol.
4330. 10064
4331. 10064 const the_symbol = symbol(id);
4332. 10064 the_symbol.constant = true;
4333. 10064 the_symbol.nud_prefix = (
4334. 10064 typeof value === "function"
4335. 4403 ? value
4336. 18668 : function () {
4337. 18668 token_now.constant = true;
4338. 5661 if (value !== undefined) {
4339. 5661 token_now.value = value;
4340. 5661 }
4341. 18668 return token_now;
4342. 18668 }
4343. 10064 );
4344. 10064 the_symbol.type = type;
4345. 10064 the_symbol.value = value;
4346. 10064 return the_symbol;
4347. 10064 }
4348. 629
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. 629
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. 629
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. 629
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. 629
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. 629
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. 629
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. 629
4433. 6360 function enroll(name, role, readonly) {
4434. 6360
4435. 6360// Enroll a name into the current function context. The role can be exception,
4436. 6360// function, label, parameter, or variable. We look for variable redefinition
4437. 6360// because it causes confusion.
4438. 6360
4439. 6360 let earlier;
4440. 6360 let id = name.id;
4441. 6360
4442. 6360// Reserved words may not be enrolled.
4443. 6360
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. 6359 }
4452. 6359
4453. 6359// Has the name been enrolled in this context?
4454. 6359
4455. 6359 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. 6352 }
4464. 6352
4465. 6352// Has the name been enrolled in an outer context?
4466. 6352
4467. 10744 function_stack.forEach(function ({
4468. 10744 context
4469. 10744 }) {
4470. 10589 earlier = context[id] || earlier;
4471. 10744 });
4472. 6352 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. 6348 } else if (
4481. 6348 earlier
4482. 6348 && role !== "parameter" && role !== "function"
4483. 6348 && (role !== "exception" || earlier.role !== "exception")
4484. 6348 ) {
4485. 6348
4486. 6348// test_cause:
4487. 6348// ["
4488. 6348// function aa(){try{aa();}catch(aa){aa();}}
4489. 6348// ", "enroll", "redefinition_a_b", "1", 31]
4490. 6348// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19]
4491. 6348
4492. 6348 warn("redefinition_a_b", name, id, earlier.line);
4493. 6348 } else if (
4494. 6348 option_dict.beta
4495. 6348 && global_dict[id]
4496. 6348 && role !== "parameter"
4497. 6348 ) {
4498. 6348
4499. 6348// test_cause:
4500. 6348// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5]
4501. 6348
4502. 6348 warn("redefinition_global_a_b", name, global_dict[id], id);
4503. 6352 }
4504. 6352
4505. 6352// Enroll it.
4506. 6352
4507. 6352 Object.assign(name, {
4508. 6352 dead: true,
4509. 6352 init: false,
4510. 6352 parent: (
4511. 6352 role === "exception"
4512. 6352 ? catchage
4513. 6322 : functionage
4514. 6360 ),
4515. 6360 readonly,
4516. 6360 role,
4517. 6360 used: 0
4518. 6360 });
4519. 6360 name.parent.context[id] = name;
4520. 6360 }
4521. 629
4522. 18870 function infix(bp, id, f) {
4523. 18870
4524. 18870// Create an infix operator.
4525. 18870
4526. 18870 const the_symbol = symbol(id, bp);
4527. 31880 the_symbol.led_infix = function (left) {
4528. 31880 const the_token = token_now;
4529. 31880 the_token.arity = "binary";
4530. 23454 if (f !== undefined) {
4531. 23454 return f(left);
4532. 23454 }
4533. 8426 the_token.expression = [left, parse_expression(bp)];
4534. 8426 return the_token;
4535. 8426 };
4536. 18870 return the_symbol;
4537. 18870 }
4538. 629
4539. 11574 function infix_dot(left) {
4540. 11574 const the_token = token_now;
4541. 11574 let name = token_nxt;
4542. 11574 if (
4543. 11574 (
4544. 11574 left.id !== "(string)"
4545. 44 || (name.id !== "indexOf" && name.id !== "repeat")
4546. 11574 )
4547. 11531 && (
4548. 11531 left.id !== "["
4549. 11531 || (
4550. 11531 name.id !== "concat"
4551. 11531 && name.id !== "flat"
4552. 11531 && name.id !== "flatMap"
4553. 11531 && name.id !== "forEach"
4554. 11531 && name.id !== "join"
4555. 11531 && name.id !== "map"
4556. 11531 )
4557. 11531 )
4558. 11486 && (left.id !== "+" || name.id !== "slice")
4559. 11481 && (
4560. 11481 left.id !== "(regexp)"
4561. 11481 || (name.id !== "exec" && name.id !== "test")
4562. 11481 )
4563. 11414 ) {
4564. 11414
4565. 11414// test_cause:
4566. 11414// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
4567. 11414
4568. 11414 check_left(left, the_token);
4569. 11414 }
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. 11573 }
4577. 11573 advance();
4578. 11573 survey(name);
4579. 11573
4580. 11573// The property name is not an expression.
4581. 11573
4582. 11573 the_token.name = name;
4583. 11573 the_token.expression = left;
4584. 11573 return the_token;
4585. 11573 }
4586. 629
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. 629
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. 629
4606. 1466 function infix_lbracket(left) {
4607. 1466 const the_token = token_now;
4608. 1466 let name;
4609. 1466 let the_subscript = parse_expression(0);
4610. 1443 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. 1466
4624. 1466// test_cause:
4625. 1466// ["0[0]", "check_left", "unexpected_a", "[", 2]
4626. 1466
4627. 1466 check_left(left, the_token);
4628. 1466 the_token.expression = [left, the_subscript];
4629. 1466 advance("]");
4630. 1466 return the_token;
4631. 1466 }
4632. 629
4633. 10402 function infix_lparen(left) {
4634. 10402 const the_paren = token_now;
4635. 10402 let ellipsis;
4636. 10402 let the_argument;
4637. 10365 if (left.id !== "function") {
4638. 10365
4639. 10365// test_cause:
4640. 10365// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
4641. 10365// ["0()", "check_left", "unexpected_a", "(", 2]
4642. 10365
4643. 10365 check_left(left, the_paren);
4644. 10365 }
4645. 7694 if (functionage.arity === "statement" && left.identifier) {
4646. 5498 functionage.name.calls[left.id] = left;
4647. 5498 }
4648. 10402 the_paren.expression = [left];
4649. 8944 if (token_nxt.id !== ")") {
4650. 8944
4651. 8944// Parse/loop through each token in expression (...).
4652. 8944
4653. 14268 while (true) {
4654. 14268 if (token_nxt.id === "...") {
4655. 14268 ellipsis = true;
4656. 14268 advance("...");
4657. 14268 }
4658. 14268 the_argument = parse_expression(10);
4659. 14268 if (ellipsis) {
4660. 14268 the_argument.ellipsis = true;
4661. 14268 }
4662. 14268 the_paren.expression.push(the_argument);
4663. 14268 if (token_nxt.id !== ",") {
4664. 14268 break;
4665. 14268 }
4666. 14268 advance(",");
4667. 14268 }
4668. 8944 }
4669. 10402 advance(")", the_paren);
4670. 5242 if (the_paren.expression.length === 2) {
4671. 5242
4672. 5242// test_cause:
4673. 5242// ["aa(0)", "infix_lparen", "free", "", 0]
4674. 5242
4675. 5242 test_cause("free");
4676. 5242 the_paren.free = true;
4677. 5242 if (the_argument.wrapped === true) {
4678. 5242
4679. 5242// test_cause:
4680. 5242// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
4681. 5242
4682. 5242 warn("unexpected_a", the_paren);
4683. 5242 }
4684. 5242 if (the_argument.id === "(") {
4685. 5242 the_argument.wrapped = true;
4686. 5242 }
4687. 5242 } else {
4688. 5160
4689. 5160// test_cause:
4690. 5160// ["aa()", "infix_lparen", "not_free", "", 0]
4691. 5160// ["aa(0,0)", "infix_lparen", "not_free", "", 0]
4692. 5160
4693. 5160 test_cause("not_free");
4694. 5160 the_paren.free = false;
4695. 5160 }
4696. 10402 return the_paren;
4697. 10402 }
4698. 629
4699. 10 function infix_option_chain(left) {
4700. 10 const the_token = token_now;
4701. 10 let name;
4702. 10 name = token_nxt;
4703. 10 if (
4704. 10 (
4705. 10 left.id !== "(string)"
4706. 1 || (name.id !== "indexOf" && name.id !== "repeat")
4707. 10 )
4708. 10 && (
4709. 10 left.id !== "["
4710. 1 || (
4711. 1 name.id !== "concat"
4712. 1 && name.id !== "forEach"
4713. 1 && name.id !== "join"
4714. 1 && name.id !== "map"
4715. 1 )
4716. 10 )
4717. 10
4718. 10// test_cause:
4719. 10// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0]
4720. 10
4721. 1 && (left.id !== "+" || name.id !== "slice")
4722. 10 && (
4723. 10 left.id !== "(regexp)"
4724. 1 || (name.id !== "exec" && name.id !== "test")
4725. 10 )
4726. 10 ) {
4727. 10 test_cause("check_left");
4728. 10
4729. 10// test_cause:
4730. 10// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
4731. 10// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
4732. 10// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
4733. 10
4734. 10 check_left(left, the_token);
4735. 10 }
4736. 4 if (!name.identifier) {
4737. 4
4738. 4// test_cause:
4739. 4// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5]
4740. 4
4741. 4 stop("expected_identifier_a");
4742. 6 }
4743. 6 advance();
4744. 6 survey(name);
4745. 6
4746. 6// The property name is not an expression.
4747. 6
4748. 6 the_token.name = name;
4749. 6 the_token.expression = left;
4750. 6 return the_token;
4751. 6 }
4752. 629
4753. 629 function infixr(bp, id) {
4754. 629
4755. 629// Create a right associative infix operator.
4756. 629
4757. 629 const the_symbol = symbol(id, bp);
4758. 1 the_symbol.led_infix = function parse_infixr_led(left) {
4759. 1 const the_token = token_now;
4760. 1
4761. 1// test_cause:
4762. 1// ["0**0", "parse_infixr_led", "led_infix", "", 0]
4763. 1
4764. 1 test_cause("led_infix");
4765. 1 the_token.arity = "binary";
4766. 1 the_token.expression = [left, parse_expression(bp - 1)];
4767. 1 return the_token;
4768. 1 };
4769. 629 return the_symbol;
4770. 629 }
4771. 629
4772. 55937 function parse_expression(rbp, initial) {
4773. 55937
4774. 55937// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
4775. 55937// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
4776. 55937// is like .nud_prefix except that it is only used on the first token of a
4777. 55937// statement. Having .fud_stmt makes it much easier to define statement-oriented
4778. 55937// languages like JavaScript. I retained Pratt's nomenclature.
4779. 55937// They are elements of the parsing method called Top Down Operator Precedence.
4780. 55937
4781. 55937// .nud_prefix Null denotation. The prefix handler.
4782. 55937// .fud_stmt First null denotation. The statement handler.
4783. 55937// .led_infix Left denotation. The infix/postfix handler.
4784. 55937// lbp Left binding power of infix operator. It tells us how strongly
4785. 55937// the operator binds to the argument at its left.
4786. 55937// rbp Right binding power.
4787. 55937
4788. 55937// It processes a nud_prefix (variable, constant, prefix operator). It will then
4789. 55937// process leds (infix operators) until the bind powers cause it to stop (it
4790. 55937// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
4791. 55937// means that it collects all tokens that bind together before returning to the
4792. 55937// operator that called it. It returns the expression's parse tree.
4793. 55937
4794. 55937// For example, "3 + 1 * 2 * 4 + 5"
4795. 55937// parses into
4796. 55937// {
4797. 55937// "id": "+",
4798. 55937// "expression": [
4799. 55937// {
4800. 55937// "id": "+",
4801. 55937// "expression": [
4802. 55937// {
4803. 55937// "id": "(number)",
4804. 55937// "value": "3"
4805. 55937// },
4806. 55937// {
4807. 55937// "id": "*",
4808. 55937// "expression": [
4809. 55937// {
4810. 55937// "id": "*",
4811. 55937// "expression": [
4812. 55937// {
4813. 55937// "id": "(number)",
4814. 55937// "value": "1"
4815. 55937// },
4816. 55937// {
4817. 55937// "id": "(number)",
4818. 55937// "value": "2"
4819. 55937// }
4820. 55937// ]
4821. 55937// },
4822. 55937// {
4823. 55937// "id": "(number)",
4824. 55937// "value": "4"
4825. 55937// }
4826. 55937// ]
4827. 55937// }
4828. 55937// ]
4829. 55937// },
4830. 55937// {
4831. 55937// "id": "(number)",
4832. 55937// "value": "5"
4833. 55937// }
4834. 55937// ]
4835. 55937// }
4836. 55937
4837. 55937 let left;
4838. 55937 let the_symbol;
4839. 55937
4840. 55937// Statements will have already advanced, so advance now only if the token is
4841. 55937// not the first of a statement.
4842. 55937
4843. 44585 if (!initial) {
4844. 44585 advance();
4845. 44585 }
4846. 55937 the_symbol = syntax_dict[token_now.id];
4847. 24489 if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
4848. 24430
4849. 24430// test_cause:
4850. 24430// ["0", "parse_expression", "symbol", "", 0]
4851. 24430
4852. 24430 test_cause("symbol");
4853. 24430 left = the_symbol.nud_prefix();
4854. 31507 } else if (token_now.identifier) {
4855. 31507
4856. 31507// test_cause:
4857. 31507// ["aa", "parse_expression", "identifier", "", 0]
4858. 31507
4859. 31507 test_cause("identifier");
4860. 31507 left = token_now;
4861. 31507 left.arity = "variable";
4862. 31507 } else {
4863. 31507
4864. 31507// test_cause:
4865. 31507// ["!", "parse_expression", "unexpected_a", "(end)", 1]
4866. 31507// ["/./", "parse_expression", "unexpected_a", "/", 1]
4867. 31507// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11]
4868. 31507
4869. 31507 return stop("unexpected_a", token_now);
4870. 55901 }
4871. 55901
4872. 55901// Parse/loop through each symbol in expression.
4873. 55901
4874. 93003 while (true) {
4875. 93003 the_symbol = syntax_dict[token_nxt.id];
4876. 93003 if (
4877. 93003 the_symbol === undefined
4878. 93003 || the_symbol.led_infix === undefined
4879. 93003 || the_symbol.lbp <= rbp
4880. 93003 ) {
4881. 93003 break;
4882. 93003 }
4883. 93003 advance();
4884. 93003 left = the_symbol.led_infix(left);
4885. 93003 }
4886. 55890 return left;
4887. 55890 }
4888. 629
4889. 14 function parse_fart(the_fart) {
4890. 14
4891. 14// Give the function properties storing its names and for observing the depth
4892. 14// of loops and switches.
4893. 14
4894. 14 Object.assign(the_fart, {
4895. 14 arity: "binary",
4896. 14 context: empty(),
4897. 14 finally: 0,
4898. 14 level: functionage.level + 1,
4899. 14 loop: 0,
4900. 14 name: anon,
4901. 14 switch: 0,
4902. 14 try: 0
4903. 14 });
4904. 14
4905. 14// PR-384 - Relax warning "function_in_loop".
4906. 14//
4907. 14// if (functionage.loop > 0) {
4908. 14
4909. 14// // test_cause:
4910. 14// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
4911. 14//
4912. 14// warn("function_in_loop", the_fart);
4913. 14// }
4914. 14
4915. 14// Push the current function context and establish a new one.
4916. 14
4917. 14 function_list.push(the_fart);
4918. 14 function_stack.push(functionage);
4919. 14 functionage = the_fart;
4920. 14
4921. 14// Parse the parameter list.
4922. 14
4923. 14 prefix_function_parameter(the_fart);
4924. 14 advance("=>");
4925. 14
4926. 14// The function's body is a block.
4927. 14
4928. 3 if (token_nxt.id === "{") {
4929. 3 if (!option_dict.fart) {
4930. 3
4931. 3// test_cause:
4932. 3// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
4933. 3
4934. 3 warn("use_function_not_fart", the_fart);
4935. 3 }
4936. 3 the_fart.block = block("body");
4937. 11 } else if (
4938. 11 syntax_dict[token_nxt.id] !== undefined
4939. 11 && syntax_dict[token_nxt.id].fud_stmt !== undefined
4940. 11 ) {
4941. 11
4942. 11// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
4943. 11
4944. 11// test_cause:
4945. 11// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5]
4946. 11
4947. 11 stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>");
4948. 11
4949. 11// The function's body is an expression.
4950. 11
4951. 11 } else {
4952. 11 the_fart.expression = parse_expression(0);
4953. 13 }
4954. 13
4955. 13// Restore the previous context.
4956. 13
4957. 13 functionage = function_stack.pop();
4958. 13 return the_fart;
4959. 13 }
4960. 629
4961. 12961 function parse_json() {
4962. 12961 let container;
4963. 12961 let is_dup;
4964. 12961 let name;
4965. 12961 let negative;
4966. 12961 switch (token_nxt.id) {
4967. 5262 case "(number)":
4968. 5262 if (!jslint_rgx_json_number.test(token_nxt.value)) {
4969. 5262
4970. 5262// test_cause:
4971. 5262// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
4972. 5262// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
4973. 5262// ["[.0]", "parse_json", "unexpected_a", ".", 2]
4974. 5262// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
4975. 5262
4976. 5262 warn("unexpected_a");
4977. 5262 }
4978. 5262 advance("(number)");
4979. 5262 return token_now;
4980. 1767 case "(string)":
4981. 1767 if (token_nxt.quote !== "\"") {
4982. 1767
4983. 1767// test_cause:
4984. 1767// ["['']", "parse_json", "unexpected_a", "'", 2]
4985. 1767
4986. 1767 warn("unexpected_a", token_nxt, token_nxt.quote);
4987. 1767 }
4988. 1767 advance("(string)");
4989. 1767 return token_now;
4990. 3 case "-":
4991. 3 negative = token_nxt;
4992. 3 negative.arity = "unary";
4993. 3 advance("-");
4994. 3
4995. 3// Recurse parse_json().
4996. 3
4997. 3 negative.expression = parse_json();
4998. 3 return negative;
4999. 1649 case "[":
5000. 1649
5001. 1649// test_cause:
5002. 1649// ["[]", "parse_json", "bracket", "", 0]
5003. 1649
5004. 1649 test_cause("bracket");
5005. 1649 container = token_nxt;
5006. 1649 container.expression = [];
5007. 1649 advance("[");
5008. 1649 if (token_nxt.id !== "]") {
5009. 3300 while (true) {
5010. 3300
5011. 3300// Recurse parse_json().
5012. 3300
5013. 3300 container.expression.push(parse_json());
5014. 3300 if (token_nxt.id !== ",") {
5015. 3300
5016. 3300// test_cause:
5017. 3300// ["[0,0]", "parse_json", "comma", "", 0]
5018. 3300
5019. 3300 test_cause("comma");
5020. 3300 break;
5021. 3300 }
5022. 3300 advance(",");
5023. 3300 }
5024. 1649 }
5025. 1649 advance("]", container);
5026. 1649 return container;
5027. 509 case "false":
5028. 511 case "null":
5029. 896 case "true":
5030. 896
5031. 896// test_cause:
5032. 896// ["[false]", "parse_json", "constant", "", 0]
5033. 896// ["[null]", "parse_json", "constant", "", 0]
5034. 896// ["[true]", "parse_json", "constant", "", 0]
5035. 896
5036. 896 test_cause("constant");
5037. 896 advance();
5038. 896 return token_now;
5039. 3379 case "{":
5040. 3379
5041. 3379// test_cause:
5042. 3379// ["{}", "parse_json", "brace", "", 0]
5043. 3379
5044. 3379 test_cause("brace");
5045. 3379 container = token_nxt;
5046. 3379
5047. 3379// Explicit empty-object required to detect "__proto__".
5048. 3379
5049. 3379 is_dup = empty();
5050. 3379 container.expression = [];
5051. 3379 advance("{");
5052. 3379 if (token_nxt.id !== "}") {
5053. 3379
5054. 3379// JSON
5055. 3379// Parse/loop through each property in {...}.
5056. 3379
5057. 9636 while (true) {
5058. 9636 if (token_nxt.quote !== "\"") {
5059. 9636
5060. 9636// test_cause:
5061. 9636// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
5062. 9636
5063. 9636 warn(
5064. 9636 "unexpected_a",
5065. 9636 token_nxt,
5066. 9636 token_nxt.quote
5067. 9636 );
5068. 9636 }
5069. 9636 name = token_nxt;
5070. 9636 advance("(string)");
5071. 9636 if (is_dup[token_now.value] !== undefined) {
5072. 9636
5073. 9636// test_cause:
5074. 9636// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
5075. 9636
5076. 9636 warn("duplicate_a", token_now);
5077. 9636 } else if (token_now.value === "__proto__") {
5078. 9636
5079. 9636// test_cause:
5080. 9636// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
5081. 9636
5082. 9636 warn("weird_property_a", token_now);
5083. 9636 } else {
5084. 9636 is_dup[token_now.value] = token_now;
5085. 9636 }
5086. 9636 advance(":");
5087. 9636 container.expression.push(
5088. 9636
5089. 9636// Recurse parse_json().
5090. 9636
5091. 9636 Object.assign(parse_json(), {
5092. 9636 label: name
5093. 9636 })
5094. 9636 );
5095. 9636 if (token_nxt.id !== ",") {
5096. 9636 break;
5097. 9636 }
5098. 9636 advance(",");
5099. 9636 }
5100. 3379 }
5101. 3379 advance("}", container);
5102. 3379 return container;
5103. 5 default:
5104. 5
5105. 5// test_cause:
5106. 5// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
5107. 5
5108. 5 stop("unexpected_a");
5109. 12961 }
5110. 12961 }
5111. 629
5112. 20899 function parse_statement() {
5113. 20899
5114. 20899// Parse a statement. Any statement may have a label, but only four statements
5115. 20899// have use for one. A statement can be one of the standard statements, or
5116. 20899// an assignment expression, or an invocation expression.
5117. 20899
5118. 20899 let first;
5119. 20899 let the_label;
5120. 20899 let the_statement;
5121. 20899 let the_symbol;
5122. 20899 advance();
5123. 20712 if (token_now.identifier && token_nxt.id === ":") {
5124. 13 the_label = token_now;
5125. 13 if (the_label.id === "ignore") {
5126. 13
5127. 13// test_cause:
5128. 13// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1]
5129. 13
5130. 13 warn("unexpected_a", the_label);
5131. 13 }
5132. 13 advance(":");
5133. 13 switch (token_nxt.id) {
5134. 13 case "do":
5135. 13 case "for":
5136. 13 case "switch":
5137. 13 case "while":
5138. 13
5139. 13// test_cause:
5140. 13// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0]
5141. 13// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0]
5142. 13// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0]
5143. 13// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0]
5144. 13
5145. 13 test_cause("the_statement_label", token_nxt.id);
5146. 13 enroll(the_label, "label", true);
5147. 13 the_label.dead = false;
5148. 13 the_label.init = true;
5149. 13 the_statement = parse_statement();
5150. 13 functionage.statement_prv = the_statement;
5151. 13 the_statement.label = the_label;
5152. 13 the_statement.statement = true;
5153. 13 return the_statement;
5154. 13 }
5155. 13 advance();
5156. 13
5157. 13// test_cause:
5158. 13// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1]
5159. 13
5160. 13 warn("unexpected_label_a", the_label);
5161. 20890 }
5162. 20890
5163. 20890// Parse the statement.
5164. 20890
5165. 20890 first = token_now;
5166. 20890 first.statement = true;
5167. 20890 the_symbol = syntax_dict[first.id];
5168. 20890 if (
5169. 20890 the_symbol !== undefined
5170. 20890 && the_symbol.fud_stmt !== undefined
5171. 20899
5172. 20899// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
5173. 20899
5174. 10132 && !(the_symbol.id === "import" && token_nxt.id === "(")
5175. 10130 ) {
5176. 10130 the_symbol.disrupt = false;
5177. 10130 the_symbol.statement = true;
5178. 10130 token_now.arity = "statement";
5179. 10130 the_statement = the_symbol.fud_stmt();
5180. 10130 functionage.statement_prv = the_statement;
5181. 10760 } else {
5182. 10760
5183. 10760// It is an expression statement.
5184. 10760
5185. 10760 the_statement = parse_expression(0, true);
5186. 10760 functionage.statement_prv = the_statement;
5187. 10760 if (the_statement.wrapped && the_statement.id !== "(") {
5188. 10760
5189. 10760// test_cause:
5190. 10760// ["(0)", "parse_statement", "unexpected_a", "(", 1]
5191. 10760
5192. 10760 warn("unexpected_a", first);
5193. 10760 }
5194. 10760 semicolon();
5195. 20796 }
5196. 20796 if (the_label !== undefined) {
5197. 1 the_label.dead = true;
5198. 20796 }
5199. 20796 return the_statement;
5200. 20796 }
5201. 629
5202. 7488 function parse_statements() {
5203. 7488
5204. 7488// Parse a list of statements. Give a warning if an unreachable statement
5205. 7488// follows a disruptive statement.
5206. 7488
5207. 7488 const statement_list = [];
5208. 7488 let a_statement;
5209. 7488 let disrupt = false;
5210. 7488
5211. 7488// Parse/loop each statement until a statement-terminator is reached.
5212. 7488
5213. 27960 while (true) {
5214. 27960 switch (token_nxt.id) {
5215. 27960 case "(end)":
5216. 27960 case "case":
5217. 27960 case "default":
5218. 27960 case "else":
5219. 27960 case "}":
5220. 27960
5221. 27960// test_cause:
5222. 27960// [";", "parse_statements", "closer", "", 0]
5223. 27960// ["case", "parse_statements", "closer", "", 0]
5224. 27960// ["default", "parse_statements", "closer", "", 0]
5225. 27960// ["else", "parse_statements", "closer", "", 0]
5226. 27960// ["}", "parse_statements", "closer", "", 0]
5227. 27960
5228. 27960 test_cause("closer");
5229. 27960 return statement_list;
5230. 27960 }
5231. 27960 a_statement = parse_statement();
5232. 27960 statement_list.push(a_statement);
5233. 27960 if (disrupt) {
5234. 27960
5235. 27960// test_cause:
5236. 27960// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16]
5237. 27960
5238. 27960 warn("unreachable_a", a_statement);
5239. 27960 }
5240. 27960 disrupt = a_statement.disrupt;
5241. 27960 }
5242. 7488 }
5243. 629
5244. 1258 function postassign(id) {
5245. 1258
5246. 1258// Create one of the postassign operators.
5247. 1258
5248. 1258 const the_symbol = symbol(id, 150);
5249. 1 the_symbol.led_infix = function (left) {
5250. 1 token_now.expression = left;
5251. 1 token_now.arity = "postassign";
5252. 1 check_mutation(token_now.expression);
5253. 1 return token_now;
5254. 1 };
5255. 1258 return the_symbol;
5256. 1258 }
5257. 629
5258. 1258 function preassign(id) {
5259. 1258
5260. 1258// Create one of the preassign operators.
5261. 1258
5262. 1258 const the_symbol = symbol(id);
5263. 2 the_symbol.nud_prefix = function () {
5264. 2 const the_token = token_now;
5265. 2 the_token.arity = "preassign";
5266. 2 the_token.expression = parse_expression(150);
5267. 2 check_mutation(the_token.expression);
5268. 2 return the_token;
5269. 2 };
5270. 1258 return the_symbol;
5271. 1258 }
5272. 629
5273. 10693 function prefix(id, f) {
5274. 10693
5275. 10693// Create a prefix operator.
5276. 10693
5277. 10693 const the_symbol = symbol(id);
5278. 5744 the_symbol.nud_prefix = function () {
5279. 5744 const the_token = token_now;
5280. 5744 the_token.arity = "unary";
5281. 4932 if (typeof f === "function") {
5282. 4932 return f();
5283. 4932 }
5284. 812 the_token.expression = parse_expression(150);
5285. 812 return the_token;
5286. 812 };
5287. 10693 return the_symbol;
5288. 10693 }
5289. 629
5290. 1 function prefix_assign_divide() {
5291. 1
5292. 1// test_cause:
5293. 1// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
5294. 1
5295. 1 stop("expected_a_b", token_now, "/\\=", "/=");
5296. 1 }
5297. 629
5298. 136 function prefix_async() {
5299. 136 let the_async = token_now;
5300. 136 let the_function;
5301. 136 token_nxt.arity = the_async.arity;
5302. 136
5303. 136// PR-414 - Parse async fart.
5304. 136
5305. 3 if (token_nxt.fart) {
5306. 3 advance("(");
5307. 3 the_function = Object.assign(token_now.fart, {
5308. 3 async: 1
5309. 3 });
5310. 3 if (!option_dict.fart) {
5311. 3
5312. 3// test_cause:
5313. 3// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8]
5314. 3
5315. 3 warn("use_function_not_fart", the_function);
5316. 3 }
5317. 3 prefix_lparen();
5318. 3
5319. 3// Parse async function.
5320. 3
5321. 133 } else {
5322. 133 advance("function");
5323. 133 the_function = Object.assign(token_now, {
5324. 133 async: 1
5325. 133 });
5326. 133 prefix_function();
5327. 133 }
5328. 3 if (the_function.async === 1) {
5329. 3
5330. 3// test_cause:
5331. 3// ["
5332. 3// async function aa(){}
5333. 3// ", "prefix_async", "missing_await_statement", "function", 7]
5334. 3
5335. 3 warn("missing_await_statement", the_function);
5336. 3 }
5337. 136 return the_function;
5338. 136 }
5339. 629
5340. 297 function prefix_await() {
5341. 297 const the_await = token_now;
5342. 297
5343. 297// PR-370 - Add top-level-await support.
5344. 297
5345. 4 if (functionage.async === 0 && functionage !== token_global) {
5346. 2
5347. 2// test_cause:
5348. 2// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18]
5349. 2// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15]
5350. 2
5351. 2 warn("unexpected_a", the_await);
5352. 295 } else {
5353. 295 functionage.async += 1;
5354. 295 }
5355. 185 if (the_await.arity === "statement") {
5356. 185
5357. 185// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
5358. 185
5359. 185 the_await.expression = parse_expression(150);
5360. 185 semicolon();
5361. 185 } else {
5362. 112 the_await.expression = parse_expression(150);
5363. 112 }
5364. 297 return the_await;
5365. 297 }
5366. 629
5367. 1 function prefix_fart() {
5368. 1
5369. 1// test_cause:
5370. 1// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
5371. 1
5372. 1 return stop("expected_a_before_b", token_now, "()", "=>");
5373. 1 }
5374. 629
5375. 2005 function prefix_function(the_function) {
5376. 11 let name = the_function && the_function.name;
5377. 1994 if (the_function === undefined) {
5378. 1994 the_function = token_now;
5379. 1994
5380. 1994// A function statement must have a name that will be in the parent's scope.
5381. 1994
5382. 1994 if (the_function.arity === "statement") {
5383. 1994 if (!token_nxt.identifier) {
5384. 1994
5385. 1994// test_cause:
5386. 1994// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
5387. 1994// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9]
5388. 1994
5389. 1994 return stop("expected_identifier_a");
5390. 1994 }
5391. 1994 name = token_nxt;
5392. 1994 enroll(name, "variable", true);
5393. 1994 the_function.name = Object.assign(name, {
5394. 1994 calls: empty(),
5395. 1994
5396. 1994// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed.
5397. 1994
5398. 1994 dead: false,
5399. 1994 init: true
5400. 1994 });
5401. 1994 advance();
5402. 1994 } else if (name === undefined) {
5403. 1994
5404. 1994// A function expression may have an optional name.
5405. 1994
5406. 1994 the_function.name = anon;
5407. 1994 if (token_nxt.identifier) {
5408. 1994 name = token_nxt;
5409. 1994 the_function.name = name;
5410. 1994 advance();
5411. 1994 }
5412. 1994 }
5413. 2003 }
5414. 2003
5415. 2003// Probably deadcode.
5416. 2003// if (mode_mega) {
5417. 2003// warn("unexpected_a", the_function);
5418. 2003// }
5419. 2003// jslint_assert(!mode_mega, `Expected !mode_mega.`);
5420. 2003
5421. 2003// PR-378 - Relax warning "function_in_loop".
5422. 2003//
5423. 2003// // Don't create functions in loops. It is inefficient, and it can lead to
5424. 2003// // scoping errors.
5425. 2003//
5426. 2003// if (functionage.loop > 0) {
5427. 2003//
5428. 2003// // test_cause:
5429. 2003// // ["
5430. 2003// // while(0){aa.map(function(){});}
5431. 2003// // ", "prefix_function", "function_in_loop", "function", 17]
5432. 2003//
5433. 2003// warn("function_in_loop", the_function);
5434. 2003// }
5435. 2003
5436. 2003// Give the function properties for storing its names and for observing the
5437. 2003// depth of loops and switches.
5438. 2003
5439. 2003 Object.assign(the_function, {
5440. 2003 async: the_function.async || 0,
5441. 2005 context: empty(),
5442. 2005 finally: 0,
5443. 2005 level: functionage.level + 1,
5444. 2005 loop: 0,
5445. 2005 statement_prv: undefined,
5446. 2005 switch: 0,
5447. 2005 try: 0
5448. 2005 });
5449. 929 if (the_function.arity !== "statement" && typeof name === "object") {
5450. 38
5451. 38// test_cause:
5452. 38// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0]
5453. 38
5454. 38 test_cause("expression", name.id);
5455. 38 enroll(name, "function", true);
5456. 38 name.dead = false;
5457. 38 name.init = true;
5458. 38 name.used = 1;
5459. 2003 }
5460. 2003
5461. 2003// PR-334 - Bugfix - fix function-redefinition not warned inside function-call.
5462. 2003// Push the current function context and establish a new one.
5463. 2003
5464. 2003 function_list.push(the_function);
5465. 2003 function_stack.push(functionage);
5466. 2003 functionage = the_function;
5467. 2003
5468. 2003// Parse the parameter list.
5469. 2003
5470. 2003 advance("(");
5471. 2003 token_now.arity = "function";
5472. 2003 prefix_function_parameter(the_function);
5473. 2003
5474. 2003// The function's body is a block.
5475. 2003
5476. 2003 the_function.block = block("body");
5477. 2003 if (
5478. 2003 the_function.arity === "statement"
5479. 2003 && token_nxt.line === token_now.line
5480. 2 ) {
5481. 2
5482. 2// test_cause:
5483. 2// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16]
5484. 2
5485. 2 return stop("unexpected_a");
5486. 1990 }
5487. 1990 if (
5488. 1990 token_nxt.id === "."
5489. 1990 || token_nxt.id === "?."
5490. 2005
5491. 2005// PR-459 - Allow destructuring-assignment after function-definition.
5492. 2005
5493. 2005 // || token_nxt.id === "["
5494. 2 ) {
5495. 2
5496. 2// test_cause:
5497. 2// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
5498. 2// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
5499. 2
5500. 2 warn("unexpected_a");
5501. 1990 }
5502. 1990
5503. 1990// Check functions are ordered.
5504. 1990
5505. 1990 check_ordered(
5506. 1990 "function",
5507. 1990 function_list.slice(
5508. 1990 function_list.indexOf(the_function) + 1
5509. 2515 ).map(function ({
5510. 2515 level,
5511. 2515 name
5512. 2515 }) {
5513. 1990 return (level === the_function.level + 1) && name;
5514. 2515 }).filter(function (name) {
5515. 2510 return option_dict.beta && name && name.id;
5516. 2515 })
5517. 1990 );
5518. 1990
5519. 1990// Restore the previous context.
5520. 1990
5521. 1990 functionage = function_stack.pop();
5522. 1990 return the_function;
5523. 1990 }
5524. 629
5525. 2017 function prefix_function_parameter(the_function) {
5526. 2017
5527. 2017// This function will parse input <parameters> at beginning of <the_function>
5528. 2017
5529. 2017 let optional;
5530. 2017 let parameters = [];
5531. 2017 let signature = ["("];
5532. 2017 let subparam;
5533. 2781 function param_enroll(name) {
5534. 2514 if (name.identifier) {
5535. 2514 enroll(name, "parameter", false);
5536. 2514 } else {
5537. 267
5538. 267// test_cause:
5539. 267// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7]
5540. 267// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7]
5541. 267
5542. 267 if (the_function.id === "=>" && !option_dict.fart) {
5543. 267 warn("use_function_not_fart", the_function);
5544. 267 }
5545. 267
5546. 267// Recurse param_enroll().
5547. 267
5548. 267 name.names.forEach(param_enroll);
5549. 267 }
5550. 2781 }
5551. 2077 function param_parse() {
5552. 2077 let ellipsis = false;
5553. 2077 let param;
5554. 227 if (token_nxt.id === "{") {
5555. 227 if (optional !== undefined) {
5556. 227
5557. 227// test_cause:
5558. 227// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18]
5559. 227
5560. 227 warn(
5561. 227 "required_a_optional_b",
5562. 227 token_nxt,
5563. 227 token_nxt.id,
5564. 227 optional.id
5565. 227 );
5566. 227 }
5567. 227 param = token_nxt;
5568. 227 param.names = [];
5569. 227 advance("{");
5570. 227 signature.push("{");
5571. 625 while (true) {
5572. 625 subparam = token_nxt;
5573. 625 if (!subparam.identifier) {
5574. 625
5575. 625// test_cause:
5576. 625// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19]
5577. 625// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14]
5578. 625
5579. 625 return stop("expected_identifier_a");
5580. 625 }
5581. 625 survey(subparam);
5582. 625 advance();
5583. 625 signature.push(subparam.id);
5584. 625 if (token_nxt.id === ":") {
5585. 625 advance(":");
5586. 625 advance();
5587. 625 token_now.label = subparam;
5588. 625 subparam = token_now;
5589. 625 if (!subparam.identifier) {
5590. 625
5591. 625// test_cause:
5592. 625// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18]
5593. 625
5594. 625 return stop(
5595. 625 "expected_identifier_a",
5596. 625 token_nxt
5597. 625 );
5598. 625 }
5599. 625 }
5600. 625
5601. 625// test_cause:
5602. 625// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0]
5603. 625
5604. 625 test_cause("equal");
5605. 625 if (token_nxt.id === "=") {
5606. 625 advance("=");
5607. 625 subparam.expression = parse_expression();
5608. 625 param.open = true;
5609. 625 }
5610. 625 param.names.push(subparam);
5611. 625 if (token_nxt.id === ",") {
5612. 625 advance(",");
5613. 625 signature.push(", ");
5614. 625 } else {
5615. 625 break;
5616. 625 }
5617. 625 }
5618. 227 parameters.push(param);
5619. 227
5620. 227// test_cause:
5621. 227// ["
5622. 227// function aa({bb,aa}){}
5623. 227// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
5624. 227
5625. 227 check_ordered("parameter", param.names);
5626. 227 advance("}");
5627. 227 signature.push("}");
5628. 227 if (token_nxt.id === ",") {
5629. 227 advance(",");
5630. 227 signature.push(", ");
5631. 227 param_parse();
5632. 227 return;
5633. 227 }
5634. 1850 } else if (token_nxt.id === "[") {
5635. 1850 if (optional !== undefined) {
5636. 1850
5637. 1850// test_cause:
5638. 1850// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18]
5639. 1850
5640. 1850 warn(
5641. 1850 "required_a_optional_b",
5642. 1850 token_nxt,
5643. 1850 token_nxt.id,
5644. 1850 optional.id
5645. 1850 );
5646. 1850 }
5647. 1850 param = token_nxt;
5648. 1850 param.names = [];
5649. 1850 advance("[");
5650. 1850 signature.push("[]");
5651. 1850 while (true) {
5652. 1850 subparam = token_nxt;
5653. 1850 if (!subparam.identifier) {
5654. 1850
5655. 1850// test_cause:
5656. 1850// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19]
5657. 1850
5658. 1850 return stop("expected_identifier_a");
5659. 1850 }
5660. 1850 advance();
5661. 1850 param.names.push(subparam);
5662. 1850
5663. 1850// test_cause:
5664. 1850// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0]
5665. 1850
5666. 1850 test_cause("id");
5667. 1850 if (token_nxt.id === "=") {
5668. 1850 advance("=");
5669. 1850 subparam.expression = parse_expression();
5670. 1850 param.open = true;
5671. 1850 }
5672. 1850 if (token_nxt.id === ",") {
5673. 1850 advance(",");
5674. 1850 } else {
5675. 1850 break;
5676. 1850 }
5677. 1850 }
5678. 1850 parameters.push(param);
5679. 1850 advance("]");
5680. 1850 if (token_nxt.id === ",") {
5681. 1850 advance(",");
5682. 1850 signature.push(", ");
5683. 1850 param_parse();
5684. 1850 return;
5685. 1850 }
5686. 1850 } else {
5687. 1850 if (token_nxt.id === "...") {
5688. 1850 ellipsis = true;
5689. 1850 signature.push("...");
5690. 1850 advance("...");
5691. 1850 if (optional !== undefined) {
5692. 1850
5693. 1850// test_cause:
5694. 1850// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21]
5695. 1850
5696. 1850 warn(
5697. 1850 "required_a_optional_b",
5698. 1850 token_nxt,
5699. 1850 token_nxt.id,
5700. 1850 optional.id
5701. 1850 );
5702. 1850 }
5703. 1850 }
5704. 1850 if (!token_nxt.identifier) {
5705. 1850
5706. 1850// test_cause:
5707. 1850// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13]
5708. 1850
5709. 1850 return stop("expected_identifier_a");
5710. 1850 }
5711. 1850 param = token_nxt;
5712. 1850 parameters.push(param);
5713. 1850 advance();
5714. 1850 signature.push(param.id);
5715. 1850 if (ellipsis) {
5716. 1850 param.ellipsis = true;
5717. 1850 } else {
5718. 1850 if (token_nxt.id === "=") {
5719. 1850 optional = param;
5720. 1850 advance("=");
5721. 1850 param.expression = parse_expression(0);
5722. 1850 } else {
5723. 1850 if (optional !== undefined) {
5724. 1850
5725. 1850// test_cause:
5726. 1850// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18]
5727. 1850
5728. 1850 warn(
5729. 1850 "required_a_optional_b",
5730. 1850 param,
5731. 1850 param.id,
5732. 1850 optional.id
5733. 1850 );
5734. 1850 }
5735. 1850 }
5736. 1850 if (token_nxt.id === ",") {
5737. 1850 advance(",");
5738. 1850 signature.push(", ");
5739. 1850 param_parse();
5740. 1850 return;
5741. 1850 }
5742. 1850 }
5743. 1850 }
5744. 2077 }
5745. 2017
5746. 2017// test_cause:
5747. 2017// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0]
5748. 2017
5749. 2017 test_cause("opener", token_now.id);
5750. 2017 token_now.free = false;
5751. 1485 if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
5752. 1485 param_parse();
5753. 2009 }
5754. 2009 advance(")");
5755. 2009 signature.push(")");
5756. 2009 parameters.forEach(param_enroll);
5757. 2009 the_function.parameters = parameters;
5758. 2009 the_function.signature = signature.join("");
5759. 2009 }
5760. 629
5761. 588 function prefix_lbrace() {
5762. 588 const seen = empty();
5763. 588 const the_brace = token_now;
5764. 588 let extra;
5765. 588 let full;
5766. 588 let id;
5767. 588 let name;
5768. 588 let the_colon;
5769. 588 let value;
5770. 588 the_brace.expression = [];
5771. 548 if (token_nxt.id !== "}") {
5772. 548
5773. 548// Parse/loop through each property in {...}.
5774. 548
5775. 1990 while (true) {
5776. 1990 name = token_nxt;
5777. 1990 advance();
5778. 1990 if (
5779. 1990 (name.id === "get" || name.id === "set")
5780. 1990 && token_nxt.identifier
5781. 1990 ) {
5782. 1990 if (!option_dict.getset) {
5783. 1990
5784. 1990// test_cause:
5785. 1990// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5]
5786. 1990
5787. 1990 warn("unexpected_a", name);
5788. 1990 }
5789. 1990 extra = name.id;
5790. 1990 full = extra + " " + token_nxt.id;
5791. 1990 name = token_nxt;
5792. 1990 advance();
5793. 1990 id = survey(name);
5794. 1990 if (seen[full] === true || seen[id] === true) {
5795. 1990
5796. 1990// test_cause:
5797. 1990// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20]
5798. 1990
5799. 1990 warn("duplicate_a", name);
5800. 1990 }
5801. 1990 seen[id] = false;
5802. 1990 seen[full] = true;
5803. 1990 } else if (name.id === "`") {
5804. 1990
5805. 1990// test_cause:
5806. 1990// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5]
5807. 1990
5808. 1990 stop("unexpected_a", name);
5809. 1990
5810. 1990 } else {
5811. 1990 id = survey(name);
5812. 1990 if (typeof seen[id] === "boolean") {
5813. 1990
5814. 1990// test_cause:
5815. 1990// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8]
5816. 1990
5817. 1990 warn("duplicate_a", name);
5818. 1990 }
5819. 1990 seen[id] = true;
5820. 1990 }
5821. 1990 if (name.identifier) {
5822. 1990 if (token_nxt.id === "}" || token_nxt.id === ",") {
5823. 1990 if (typeof extra === "string") {
5824. 1990
5825. 1990// test_cause:
5826. 1990// ["aa={get aa}", "prefix_lbrace", "closer", "", 0]
5827. 1990
5828. 1990 test_cause("closer");
5829. 1990 advance("(");
5830. 1990 }
5831. 1990 value = parse_expression(Infinity, true);
5832. 1990 } else if (token_nxt.id === "(") {
5833. 1990
5834. 1990// test_cause:
5835. 1990// ["aa={aa()}", "prefix_lbrace", "paren", "", 0]
5836. 1990// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0]
5837. 1990
5838. 1990 test_cause("paren");
5839. 1990 value = prefix_function({
5840. 1990 arity: "unary",
5841. 1990 from: name.from,
5842. 1990 id: "function",
5843. 1990 line: name.line,
5844. 1990 name: (
5845. 1990 typeof extra === "string"
5846. 1990 ? extra
5847. 1990 : id
5848. 1990 ),
5849. 1990 thru: name.from
5850. 1990 });
5851. 1990 } else {
5852. 1990 if (typeof extra === "string") {
5853. 1990
5854. 1990// test_cause:
5855. 1990// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0]
5856. 1990
5857. 1990 test_cause("paren");
5858. 1990 advance("(");
5859. 1990 }
5860. 1990 the_colon = token_nxt;
5861. 1990 advance(":");
5862. 1990 value = parse_expression(0);
5863. 1990 if (
5864. 1990 value.id === name.id
5865. 1990 && value.id !== "function"
5866. 1990 ) {
5867. 1990
5868. 1990// test_cause:
5869. 1990// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7]
5870. 1990
5871. 1990 warn("unexpected_a", the_colon, ": " + name.id);
5872. 1990 }
5873. 1990 }
5874. 1990 value.label = name;
5875. 1990 if (typeof extra === "string") {
5876. 1990 value.extra = extra;
5877. 1990 }
5878. 1990 the_brace.expression.push(value);
5879. 1990 } else {
5880. 1990
5881. 1990// test_cause:
5882. 1990// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0]
5883. 1990
5884. 1990 test_cause("colon");
5885. 1990 advance(":");
5886. 1990 value = parse_expression(0);
5887. 1990 value.label = name;
5888. 1990 the_brace.expression.push(value);
5889. 1990 }
5890. 1990 if (token_nxt.id !== ",") {
5891. 1990 break;
5892. 1990 }
5893. 1990
5894. 1990// test_cause:
5895. 1990// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0]
5896. 1990
5897. 1990 test_cause("comma");
5898. 1990 advance(",");
5899. 1990 if (token_nxt.id === "}") {
5900. 1990
5901. 1990// test_cause:
5902. 1990// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13]
5903. 1990
5904. 1990 warn("unexpected_a", token_now);
5905. 1990 break;
5906. 1990 }
5907. 1990 }
5908. 583 }
5909. 583
5910. 583// test_cause:
5911. 583// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
5912. 583
5913. 583 check_ordered(
5914. 583 "property",
5915. 1985 the_brace.expression.map(function ({
5916. 1985 label
5917. 1985 }) {
5918. 1985 return label;
5919. 1985 })
5920. 583 );
5921. 583 advance("}");
5922. 583 return the_brace;
5923. 583 }
5924. 629
5925. 759 function prefix_lbracket() {
5926. 759 const the_token = token_now;
5927. 759 let element;
5928. 759 let ellipsis;
5929. 759 the_token.expression = [];
5930. 392 if (token_nxt.id !== "]") {
5931. 392
5932. 392// Parse/loop through each element in [...].
5933. 392
5934. 1884 while (true) {
5935. 1884 ellipsis = false;
5936. 1884 if (token_nxt.id === "...") {
5937. 1884 ellipsis = true;
5938. 1884 advance("...");
5939. 1884 }
5940. 1884 element = parse_expression(10);
5941. 1884 if (ellipsis) {
5942. 1884 element.ellipsis = true;
5943. 1884 }
5944. 1884 the_token.expression.push(element);
5945. 1884 if (token_nxt.id !== ",") {
5946. 1884 break;
5947. 1884 }
5948. 1884 advance(",");
5949. 1884 if (token_nxt.id === "]") {
5950. 1884
5951. 1884// test_cause:
5952. 1884// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10]
5953. 1884
5954. 1884 warn("unexpected_a", token_now);
5955. 1884 break;
5956. 1884 }
5957. 1884 }
5958. 392 }
5959. 759 advance("]");
5960. 759 return the_token;
5961. 759 }
5962. 629
5963. 1616 function prefix_lparen() {
5964. 1616 let the_paren = token_now;
5965. 1616 let the_value;
5966. 1616
5967. 1616// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
5968. 1616
5969. 14 if (token_now.fart) {
5970. 14 return parse_fart(token_now.fart);
5971. 1602 }
5972. 1602
5973. 1602// test_cause:
5974. 1602// ["(0)", "prefix_lparen", "expr", "", 0]
5975. 1602
5976. 1602 test_cause("expr");
5977. 1602 the_paren.free = true;
5978. 1602 the_value = parse_expression(0);
5979. 1602 if (the_value.wrapped === true) {
5980. 1
5981. 1// test_cause:
5982. 1// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
5983. 1
5984. 1 warn("unexpected_a", the_paren);
5985. 1602 }
5986. 1602 the_value.wrapped = true;
5987. 1602 advance(")", the_paren);
5988. 1602 return the_value;
5989. 1602 }
5990. 629
5991. 155 function prefix_new() {
5992. 155 const the_new = token_now;
5993. 155 let right;
5994. 155 right = parse_expression(160);
5995. 1 if (token_nxt.id !== "(") {
5996. 1
5997. 1// test_cause:
5998. 1// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1]
5999. 1
6000. 1 warn("expected_a_before_b", token_nxt, "()", artifact());
6001. 1 }
6002. 155 the_new.expression = right;
6003. 155 return the_new;
6004. 155 }
6005. 629
6006. 782 function prefix_tick() {
6007. 782 const the_tick = token_now;
6008. 782 the_tick.value = [];
6009. 782 the_tick.expression = [];
6010. 782 if (token_nxt.id !== "`") {
6011. 782
6012. 782// Parse/loop through each token in `${...}`.
6013. 782
6014. 1499 while (true) {
6015. 1499 advance("(string)");
6016. 1499 the_tick.value.push(token_now);
6017. 1499 if (token_nxt.id !== "${") {
6018. 1499 break;
6019. 1499 }
6020. 1499 advance("${");
6021. 1499
6022. 1499// test_cause:
6023. 1499// ["let aa=`${}`;", "prefix_tick", "${", "", 0]
6024. 1499
6025. 1499 test_cause("${");
6026. 1499 the_tick.expression.push(parse_expression(0));
6027. 1499 advance("}");
6028. 1499 }
6029. 780 }
6030. 780 advance("`");
6031. 780 return the_tick;
6032. 780 }
6033. 629
6034. 2 function prefix_void() {
6035. 2 const the_void = token_now;
6036. 2
6037. 2// test_cause:
6038. 2// ["void 0", "prefix_void", "unexpected_a", "void", 1]
6039. 2// ["void", "prefix_void", "unexpected_a", "void", 1]
6040. 2
6041. 2 warn("unexpected_a", the_void);
6042. 2 the_void.expression = parse_expression(0);
6043. 2 return the_void;
6044. 2 }
6045. 629
6046. 13416 function semicolon() {
6047. 13416
6048. 13416// Try to match a semicolon.
6049. 13416
6050. 13193 if (token_nxt.id === ";") {
6051. 13193 advance(";");
6052. 13193 } else {
6053. 223
6054. 223// test_cause:
6055. 223// ["0", "semicolon", "expected_a_b", "(end)", 1]
6056. 223
6057. 223 warn_at(
6058. 223 "expected_a_b",
6059. 223 token_now.line,
6060. 223 token_now.thru + 1,
6061. 223 ";",
6062. 223 artifact()
6063. 223 );
6064. 223 }
6065. 13416 anon = "anonymous";
6066. 13416 }
6067. 629
6068. 14467 function stmt(id, fud_stmt) {
6069. 14467
6070. 14467// Create a statement.
6071. 14467
6072. 14467 const the_symbol = symbol(id);
6073. 14467 the_symbol.fud_stmt = fud_stmt;
6074. 14467 return the_symbol;
6075. 14467 }
6076. 629
6077. 1023 function stmt_break() {
6078. 1023 const the_break = token_now;
6079. 1023 let the_label;
6080. 1023 if (
6081. 719 (functionage.loop < 1 && functionage.switch < 1)
6082. 1017 || functionage.finally > 0
6083. 6 ) {
6084. 6
6085. 6// test_cause:
6086. 6// ["break", "stmt_break", "unexpected_a", "break", 1]
6087. 6
6088. 6 warn("unexpected_a", the_break);
6089. 6 }
6090. 1023 the_break.disrupt = true;
6091. 5 if (token_nxt.identifier && token_now.line === token_nxt.line) {
6092. 5 the_label = functionage.context[token_nxt.id];
6093. 5 if (
6094. 5 the_label === undefined
6095. 5 || the_label.role !== "label"
6096. 5 || the_label.dead
6097. 5 ) {
6098. 5 if (the_label !== undefined && the_label.dead) {
6099. 5
6100. 5// test_cause:
6101. 5// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27]
6102. 5
6103. 5 warn("out_of_scope_a");
6104. 5 } else {
6105. 5
6106. 5// test_cause:
6107. 5// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11]
6108. 5
6109. 5 warn("not_label_a");
6110. 5 }
6111. 5 } else {
6112. 5 the_label.used += 1;
6113. 5 }
6114. 5 the_break.label = token_nxt;
6115. 5 advance();
6116. 5 }
6117. 1023 advance(";");
6118. 1023 return the_break;
6119. 1023 }
6120. 629
6121. 2 function stmt_continue() {
6122. 2 const the_continue = token_now;
6123. 1 if (functionage.loop < 1 || functionage.finally > 0) {
6124. 2
6125. 2// test_cause:
6126. 2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
6127. 2// ["
6128. 2// function aa(){while(0){try{}finally{continue}}}
6129. 2// ", "stmt_continue", "unexpected_a", "continue", 37]
6130. 2
6131. 2 warn("unexpected_a", the_continue);
6132. 2 }
6133. 2 check_not_top_level(the_continue);
6134. 2 the_continue.disrupt = true;
6135. 2 warn("unexpected_a", the_continue);
6136. 2 advance(";");
6137. 2 return the_continue;
6138. 2 }
6139. 629
6140. 3 function stmt_debugger() {
6141. 3 const the_debug = token_now;
6142. 1 if (!option_dict.devel) {
6143. 1
6144. 1// test_cause:
6145. 1// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
6146. 1
6147. 1 warn("unexpected_a", the_debug);
6148. 1 }
6149. 3 semicolon();
6150. 3 return the_debug;
6151. 3 }
6152. 629
6153. 72 function stmt_delete() {
6154. 72 const the_token = token_now;
6155. 72 const the_value = parse_expression(0);
6156. 72 if (
6157. 1 (the_value.id !== "." && the_value.id !== "[")
6158. 71 || the_value.arity !== "binary"
6159. 1 ) {
6160. 1
6161. 1// test_cause:
6162. 1// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
6163. 1
6164. 1 stop("expected_a_b", the_value, ".", artifact(the_value));
6165. 71 }
6166. 71 the_token.expression = the_value;
6167. 71 semicolon();
6168. 71 return the_token;
6169. 71 }
6170. 629
6171. 5 function stmt_do() {
6172. 5 const the_do = token_now;
6173. 5 check_not_top_level(the_do);
6174. 5 functionage.loop += 1;
6175. 5 the_do.block = block();
6176. 5 advance("while");
6177. 5 the_do.expression = condition();
6178. 5 semicolon();
6179. 1 if (the_do.block.disrupt === true) {
6180. 1
6181. 1// test_cause:
6182. 1// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15]
6183. 1
6184. 1 warn("weird_loop", the_do);
6185. 3 }
6186. 3 functionage.loop -= 1;
6187. 3 return the_do;
6188. 3 }
6189. 629
6190. 24 function stmt_export() {
6191. 24 let export_list = [];
6192. 24 let the_export = token_now;
6193. 24 let the_id;
6194. 24 let the_name;
6195. 24 let the_thing;
6196. 24
6197. 24 the_export.expression = [];
6198. 11 if (token_nxt.id === "default") {
6199. 11 if (export_dict.default !== undefined) {
6200. 11
6201. 11// test_cause:
6202. 11// ["
6203. 11// export default 0;export default 0
6204. 11// ", "stmt_export", "duplicate_a", "default", 25]
6205. 11
6206. 11 warn("duplicate_a");
6207. 11 }
6208. 11 advance("default");
6209. 11 the_thing = parse_expression(0);
6210. 11 if (
6211. 11 the_thing.id !== "("
6212. 11 || the_thing.expression[0].id !== "."
6213. 11 || the_thing.expression[0].expression.id !== "Object"
6214. 11 || the_thing.expression[0].name.id !== "freeze"
6215. 11 ) {
6216. 11
6217. 11// test_cause:
6218. 11// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
6219. 11
6220. 11 warn("freeze_exports", the_thing);
6221. 11
6222. 11// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
6223. 11
6224. 11 } else {
6225. 11
6226. 11// test_cause:
6227. 11// ["
6228. 11// export default Object.freeze({})
6229. 11// ", "semicolon", "expected_a_b", "(end)", 32]
6230. 11
6231. 11 semicolon();
6232. 11 }
6233. 11 export_dict.default = the_thing;
6234. 11 the_export.expression.push(the_thing);
6235. 13 } else {
6236. 13
6237. 13// PR-439 - Add grammar for "export async function ...".
6238. 13
6239. 13 if (token_nxt.id === "function" || token_nxt.id === "async") {
6240. 13
6241. 13// test_cause:
6242. 13// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8]
6243. 13// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
6244. 13
6245. 13 warn("freeze_exports");
6246. 13 the_thing = parse_statement();
6247. 13 the_name = the_thing.name;
6248. 13 the_id = the_name.id;
6249. 13 the_name.used += 1;
6250. 13 if (export_dict[the_id] !== undefined) {
6251. 13
6252. 13// test_cause:
6253. 13// ["
6254. 13// let aa;export{aa};export function aa(){}
6255. 13// ", "stmt_export", "duplicate_a", "aa", 35]
6256. 13
6257. 13 warn("duplicate_a", the_name);
6258. 13 }
6259. 13 export_dict[the_id] = the_thing;
6260. 13 the_export.expression.push(the_thing);
6261. 13 the_thing.statement = false;
6262. 13 the_thing.arity = "unary";
6263. 13 } else if (
6264. 13 token_nxt.id === "var"
6265. 13 || token_nxt.id === "let"
6266. 13 || token_nxt.id === "const"
6267. 13 ) {
6268. 13
6269. 13// test_cause:
6270. 13// ["export const", "stmt_export", "unexpected_a", "const", 8]
6271. 13// ["export let", "stmt_export", "unexpected_a", "let", 8]
6272. 13// ["export var", "stmt_export", "unexpected_a", "var", 8]
6273. 13
6274. 13 warn("unexpected_a");
6275. 13 parse_statement();
6276. 13 } else if (token_nxt.id === "{") {
6277. 13
6278. 13// test_cause:
6279. 13// ["export {}", "stmt_export", "advance{", "", 0]
6280. 13
6281. 13 test_cause("advance{");
6282. 13 advance("{");
6283. 13 while (true) {
6284. 13 if (!token_nxt.identifier) {
6285. 13
6286. 13// test_cause:
6287. 13// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
6288. 13
6289. 13 stop("expected_identifier_a");
6290. 13 }
6291. 13 the_id = token_nxt.id;
6292. 13 export_list.push(token_nxt);
6293. 13 the_name = token_global.context[the_id];
6294. 13 if (the_name === undefined) {
6295. 13
6296. 13// test_cause:
6297. 13// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
6298. 13
6299. 13 warn("unexpected_a");
6300. 13 } else {
6301. 13 the_name.used += 1;
6302. 13 if (export_dict[the_id] !== undefined) {
6303. 13
6304. 13// test_cause:
6305. 13// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
6306. 13
6307. 13 warn("duplicate_a");
6308. 13 }
6309. 13 export_dict[the_id] = the_name;
6310. 13 }
6311. 13 advance();
6312. 13 the_export.expression.push(the_thing);
6313. 13 if (token_nxt.id === ",") {
6314. 13 advance(",");
6315. 13 } else {
6316. 13 break;
6317. 13 }
6318. 13 }
6319. 13
6320. 13// PR-439 - Check exported properties are ordered.
6321. 13
6322. 13// test_cause:
6323. 13// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13]
6324. 13
6325. 13 check_ordered("export", export_list);
6326. 13 advance("}");
6327. 13 semicolon();
6328. 13 } else {
6329. 13
6330. 13// test_cause:
6331. 13// ["export", "stmt_export", "unexpected_a", "(end)", 1]
6332. 13
6333. 13 stop("unexpected_a");
6334. 13 }
6335. 18 }
6336. 18 state.mode_module = true;
6337. 18 return the_export;
6338. 18 }
6339. 629
6340. 12 function stmt_for() {
6341. 12 let first;
6342. 12 let the_for = token_now;
6343. 7 if (!option_dict.for) {
6344. 7
6345. 7// test_cause:
6346. 7// ["for", "stmt_for", "unexpected_a", "for", 1]
6347. 7
6348. 7 warn("unexpected_a", the_for);
6349. 7 }
6350. 12 check_not_top_level(the_for);
6351. 12 functionage.loop += 1;
6352. 12 advance("(");
6353. 12 token_now.free = true;
6354. 1 if (token_nxt.id === ";") {
6355. 1
6356. 1// test_cause:
6357. 1// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1]
6358. 1
6359. 1 return stop("expected_a_b", the_for, "while (", "for (;");
6360. 9 }
6361. 9 switch (token_nxt.id) {
6362. 9 case "const":
6363. 1 case "let":
6364. 1 case "var":
6365. 1
6366. 1// test_cause:
6367. 1// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5]
6368. 1
6369. 1 return stop("unexpected_a");
6370. 8 }
6371. 8 first = parse_expression(0);
6372. 8 if (first.id === "in") {
6373. 2 if (first.expression[0].arity !== "variable") {
6374. 2
6375. 2// test_cause:
6376. 2// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5]
6377. 2
6378. 2 warn("bad_assignment_a", first.expression[0]);
6379. 2 }
6380. 2 the_for.name = first.expression[0];
6381. 2 the_for.expression = first.expression[1];
6382. 2 warn("expected_a_b", the_for, "Object.keys", "for in");
6383. 6 } else {
6384. 6 the_for.initial = first;
6385. 6 advance(";");
6386. 6 the_for.expression = parse_expression(0);
6387. 6 advance(";");
6388. 6 the_for.inc = parse_expression(0);
6389. 6 if (the_for.inc.id === "++") {
6390. 6
6391. 6// test_cause:
6392. 6// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13]
6393. 6
6394. 6 warn("expected_a_b", the_for.inc, "+= 1", "++");
6395. 6 }
6396. 8 }
6397. 8 advance(")");
6398. 8 the_for.block = block();
6399. 8 if (the_for.block.disrupt === true) {
6400. 1
6401. 1// test_cause:
6402. 1// ["
6403. 1// /*jslint for*/
6404. 1// function aa(bb,cc){for(0;0;0){break;}}
6405. 1// ", "stmt_for", "weird_loop", "for", 20]
6406. 1
6407. 1 warn("weird_loop", the_for);
6408. 8 }
6409. 8 functionage.loop -= 1;
6410. 8 return the_for;
6411. 8 }
6412. 629
6413. 3040 function stmt_if() {
6414. 3040 const the_if = token_now;
6415. 3040 let the_else;
6416. 3040 the_if.expression = condition();
6417. 3040 the_if.block = block();
6418. 642 if (token_nxt.id === "else") {
6419. 642 advance("else");
6420. 642 the_else = token_now;
6421. 642 the_if.else = (
6422. 642 token_nxt.id === "if"
6423. 642 ? parse_statement()
6424. 642 : block()
6425. 642 );
6426. 642
6427. 642// test_cause:
6428. 642// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0]
6429. 642// ["if(0){0}else{0}", "stmt_if", "else", "", 0]
6430. 642
6431. 642 test_cause("else");
6432. 642 if (the_if.block.disrupt === true) {
6433. 642 if (the_if.else.disrupt === true) {
6434. 642
6435. 642// test_cause:
6436. 642// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0]
6437. 642
6438. 642 test_cause("disrupt");
6439. 642 the_if.disrupt = true;
6440. 642 } else {
6441. 642
6442. 642// test_cause:
6443. 642// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
6444. 642
6445. 642 warn("unexpected_a", the_else);
6446. 642 }
6447. 642 }
6448. 3039 }
6449. 3039 return the_if;
6450. 3039 }
6451. 629
6452. 62 function stmt_import() {
6453. 62 const the_import = token_now;
6454. 62 let name;
6455. 62 let names;
6456. 62
6457. 62// PR-347 - Disable warning "unexpected_directive_a".
6458. 62//
6459. 62// if (typeof state.mode_module === "object") {
6460. 62//
6461. 62// // test_cause:
6462. 62// // ["
6463. 62// // /*global aa*/
6464. 62// // import aa from "aa"
6465. 62// // ", "stmt_import", "unexpected_directive_a", "global", 1]
6466. 62//
6467. 62// warn(
6468. 62// "unexpected_directive_a",
6469. 62// state.mode_module,
6470. 62// state.mode_module.directive
6471. 62// );
6472. 62// }
6473. 62
6474. 62 state.mode_module = true;
6475. 62
6476. 62// PR-436 - Add grammar for side-effect import-statement.
6477. 62
6478. 1 if (token_nxt.id === "(string)") {
6479. 1
6480. 1// test_cause:
6481. 1// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0]
6482. 1
6483. 1 test_cause("import_side_effect");
6484. 1 warn("expected_a_b", token_nxt, "{", artifact());
6485. 1 advance();
6486. 1 semicolon();
6487. 1 return the_import;
6488. 61 }
6489. 61 if (token_nxt.identifier) {
6490. 57 name = token_nxt;
6491. 57 advance();
6492. 57 if (name.id === "ignore") {
6493. 57
6494. 57// test_cause:
6495. 57// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
6496. 57
6497. 57 warn("unexpected_a", name);
6498. 57 }
6499. 57 enroll(name, "variable", true);
6500. 57 the_import.name = name;
6501. 57 } else {
6502. 4 names = [];
6503. 4 advance("{");
6504. 4 if (token_nxt.id !== "}") {
6505. 4 while (true) {
6506. 4 if (!token_nxt.identifier) {
6507. 4
6508. 4// test_cause:
6509. 4// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1]
6510. 4
6511. 4 stop("expected_identifier_a");
6512. 4 }
6513. 4 name = token_nxt;
6514. 4 advance();
6515. 4 if (name.id === "ignore") {
6516. 4
6517. 4// test_cause:
6518. 4// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
6519. 4
6520. 4 warn("unexpected_a", name);
6521. 4 }
6522. 4 enroll(name, "variable", true);
6523. 4 names.push(name);
6524. 4 if (token_nxt.id !== ",") {
6525. 4 break;
6526. 4 }
6527. 4 advance(",");
6528. 4 }
6529. 4 }
6530. 4 advance("}");
6531. 4 the_import.name = names;
6532. 60 }
6533. 60 advance("from");
6534. 60 advance("(string)");
6535. 60 the_import.import = token_now;
6536. 60 if (!jslint_rgx_module.test(token_now.value)) {
6537. 1
6538. 1// test_cause:
6539. 1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
6540. 1
6541. 1 warn("bad_module_name_a", token_now);
6542. 60 }
6543. 60 import_list.push(token_now.value);
6544. 60 semicolon();
6545. 60 return the_import;
6546. 60 }
6547. 629
6548. 5 function stmt_lbrace() {
6549. 5
6550. 5// test_cause:
6551. 5// [";{}", "stmt_lbrace", "naked_block", "{", 2]
6552. 5// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
6553. 5
6554. 5 warn("naked_block", token_now);
6555. 5 return block("naked");
6556. 5 }
6557. 629
6558. 1756 function stmt_return() {
6559. 1756 const the_return = token_now;
6560. 1756 check_not_top_level(the_return);
6561. 1 if (functionage.finally > 0) {
6562. 1
6563. 1// test_cause:
6564. 1// ["
6565. 1// function aa(){try{}finally{return;}}
6566. 1// ", "stmt_return", "unexpected_a", "return", 28]
6567. 1
6568. 1 warn("unexpected_a", the_return);
6569. 1 }
6570. 1756 the_return.disrupt = true;
6571. 1504 if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
6572. 1504 the_return.expression = parse_expression(10);
6573. 1504 }
6574. 1756 advance(";");
6575. 1756 return the_return;
6576. 1756 }
6577. 629
6578. 5 function stmt_semicolon() {
6579. 5
6580. 5// test_cause:
6581. 5// [";", "stmt_semicolon", "unexpected_a", ";", 1]
6582. 5
6583. 5 warn("unexpected_a", token_now);
6584. 5 return token_now;
6585. 5 }
6586. 629
6587. 220 function stmt_switch() {
6588. 220 const the_cases = [];
6589. 220 const the_switch = token_now;
6590. 220 let dups = [];
6591. 220 let exp;
6592. 220 let last;
6593. 220 let stmts;
6594. 220 let the_case;
6595. 220 let the_default;
6596. 220 let the_disrupt = true;
6597. 220 let the_last;
6598. 23088 function is_dup(thing) {
6599. 23088 return is_equal(thing, exp);
6600. 23088 }
6601. 220 check_not_top_level(the_switch);
6602. 1 if (functionage.finally > 0) {
6603. 1
6604. 1// test_cause:
6605. 1// ["
6606. 1// function aa(){try{}finally{switch(0){}}}
6607. 1// ", "stmt_switch", "unexpected_a", "switch", 28]
6608. 1
6609. 1 warn("unexpected_a", the_switch);
6610. 1 }
6611. 220 functionage.switch += 1;
6612. 220 advance("(");
6613. 220 token_now.free = true;
6614. 220 the_switch.expression = parse_expression(0);
6615. 220 the_switch.block = the_cases;
6616. 220 advance(")");
6617. 220 advance("{");
6618. 1085 while (true) {
6619. 1085
6620. 1085// Loop through cases with breaks.
6621. 1085
6622. 1085 the_case = token_nxt;
6623. 1085 the_case.arity = "statement";
6624. 1085 the_case.expression = [];
6625. 1612 while (true) {
6626. 1612
6627. 1612// Loop through fallthrough cases.
6628. 1612
6629. 1612 advance("case");
6630. 1612 token_now.switch = true;
6631. 1612 exp = parse_expression(0);
6632. 1612 if (dups.some(is_dup)) {
6633. 1612
6634. 1612// test_cause:
6635. 1612// ["
6636. 1612// switch(0){case 0:break;case 0:break}
6637. 1612// ", "stmt_switch", "unexpected_a", "0", 29]
6638. 1612
6639. 1612 warn("unexpected_a", exp);
6640. 1612 }
6641. 1612 dups.push(exp);
6642. 1612 the_case.expression.push(exp);
6643. 1612 advance(":");
6644. 1612 if (token_nxt.id !== "case") {
6645. 1612 break;
6646. 1612 }
6647. 1612 }
6648. 1085
6649. 1085// test_cause:
6650. 1085// ["
6651. 1085// switch(0){case 1:case 0:break;}
6652. 1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
6653. 1085// ["
6654. 1085// switch(0){case "aa":case 0:break;}
6655. 1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
6656. 1085// ["
6657. 1085// switch(0){case "bb":case "aa":break;}
6658. 1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
6659. 1085// ["
6660. 1085// switch(0){case aa:case "aa":break;}
6661. 1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
6662. 1085// ["
6663. 1085// switch(0){case bb:case aa:break;}
6664. 1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
6665. 1085
6666. 1085 check_ordered_case(the_case.expression);
6667. 1085 stmts = parse_statements();
6668. 1085 if (stmts.length < 1) {
6669. 1085
6670. 1085// test_cause:
6671. 1085// ["
6672. 1085// switch(0){case 0:default:}
6673. 1085// ", "stmt_switch", "expected_statements_a", "default", 18]
6674. 1085// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
6675. 1085
6676. 1085 warn("expected_statements_a");
6677. 1085
6678. 1085// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
6679. 1085
6680. 1085 break;
6681. 1085 }
6682. 1085 the_case.block = stmts;
6683. 1085 the_cases.push(the_case);
6684. 1085 last = stmts[stmts.length - 1];
6685. 1085 if (last.disrupt) {
6686. 1085 if (last.id === "break" && last.label === undefined) {
6687. 1085 the_disrupt = false;
6688. 1085 }
6689. 1085 } else {
6690. 1085 warn("expected_a_before_b", token_nxt, "break;", artifact());
6691. 1085 }
6692. 1085 if (token_nxt.id !== "case") {
6693. 1085 break;
6694. 1085 }
6695. 1085 }
6696. 217
6697. 217// test_cause:
6698. 217// ["
6699. 217// switch(0){case 1:break;case 0:break;}
6700. 217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
6701. 217// ["
6702. 217// switch(0){case "aa":break;case 0:break;}
6703. 217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
6704. 217// ["
6705. 217// switch(0){case "bb":break;case "aa":break;}
6706. 217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
6707. 217// ["
6708. 217// switch(0){case aa:break;case "aa":break;}
6709. 217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
6710. 217// ["
6711. 217// switch(0){case bb:break;case aa:break;}
6712. 217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
6713. 217
6714. 1080 check_ordered_case(the_cases.map(function ({
6715. 1080 expression
6716. 1080 }) {
6717. 1080 return expression[0];
6718. 1080 }));
6719. 217 dups = undefined;
6720. 217 if (token_nxt.id === "default") {
6721. 99 the_default = token_nxt;
6722. 99 advance("default");
6723. 99 token_now.switch = true;
6724. 99 advance(":");
6725. 99 the_switch.else = parse_statements();
6726. 99 if (the_switch.else.length < 1) {
6727. 99
6728. 99// test_cause:
6729. 99// ["
6730. 99// switch(0){case 0:break;default:}
6731. 99// ", "stmt_switch", "unexpected_a", "default", 24]
6732. 99
6733. 99 warn("unexpected_a", the_default);
6734. 99 the_disrupt = false;
6735. 99 } else {
6736. 99 the_last = the_switch.else[
6737. 99 the_switch.else.length - 1
6738. 99 ];
6739. 99 if (
6740. 99 the_last.id === "break"
6741. 99 && the_last.label === undefined
6742. 99 ) {
6743. 99
6744. 99// test_cause:
6745. 99// ["
6746. 99// switch(0){case 0:break;default:break;}
6747. 99// ", "stmt_switch", "unexpected_a", "break", 32]
6748. 99
6749. 99 warn("unexpected_a", the_last);
6750. 99 the_last.disrupt = false;
6751. 99 }
6752. 99 the_disrupt = the_disrupt && the_last.disrupt;
6753. 99 }
6754. 118 } else {
6755. 118 the_disrupt = false;
6756. 217 }
6757. 217 advance("}", the_switch);
6758. 217 functionage.switch -= 1;
6759. 217 the_switch.disrupt = the_disrupt;
6760. 217 return the_switch;
6761. 217 }
6762. 629
6763. 40 function stmt_throw() {
6764. 40 const the_throw = token_now;
6765. 40 the_throw.disrupt = true;
6766. 40 the_throw.expression = parse_expression(10);
6767. 40 semicolon();
6768. 1 if (functionage.try > 0) {
6769. 1
6770. 1// test_cause:
6771. 1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
6772. 1
6773. 1 warn("unexpected_a", the_throw);
6774. 1 }
6775. 40 return the_throw;
6776. 40 }
6777. 629
6778. 62 function stmt_try() {
6779. 62 const the_try = token_now;
6780. 62 let ignored;
6781. 62 let the_catch;
6782. 62 let the_disrupt;
6783. 1 if (functionage.try > 0) {
6784. 1
6785. 1// test_cause:
6786. 1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
6787. 1
6788. 1 warn("unexpected_a", the_try);
6789. 1 }
6790. 62 functionage.try += 1;
6791. 62 the_try.block = block();
6792. 62 the_disrupt = the_try.block.disrupt;
6793. 57 if (token_nxt.id === "catch") {
6794. 57 ignored = "ignore";
6795. 57 the_catch = token_nxt;
6796. 57 the_try.catch = the_catch;
6797. 57 advance("catch");
6798. 57
6799. 57// Create new catch-scope for catch-parameter.
6800. 57
6801. 57 catch_stack.push(catchage);
6802. 57 catchage = the_catch;
6803. 57 catch_list.push(catchage);
6804. 57 the_catch.context = empty();
6805. 57 if (token_nxt.id === "(") {
6806. 57 advance("(");
6807. 57 if (!token_nxt.identifier) {
6808. 57
6809. 57// test_cause:
6810. 57// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
6811. 57
6812. 57 return stop("expected_identifier_a");
6813. 57 }
6814. 57 if (token_nxt.id !== "ignore") {
6815. 57 ignored = undefined;
6816. 57 the_catch.name = token_nxt;
6817. 57 enroll(token_nxt, "exception", true);
6818. 57 }
6819. 57 advance();
6820. 57 advance(")");
6821. 57 }
6822. 57 the_catch.block = block(ignored);
6823. 57 if (the_catch.block.disrupt !== true) {
6824. 57 the_disrupt = false;
6825. 57 }
6826. 57
6827. 57// Restore previous catch-scope after catch-block.
6828. 57
6829. 57 catchage = catch_stack.pop();
6830. 57
6831. 57// PR-404 - Relax warning about missing `catch` in `try...finally` statement.
6832. 57//
6833. 57// } else {
6834. 57//
6835. 57// // test_cause:
6836. 57// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
6837. 57//
6838. 57// warn("expected_a_before_b", token_nxt, "catch", artifact());
6839. 57
6840. 58 }
6841. 58 if (token_nxt.id === "finally") {
6842. 4 functionage.finally += 1;
6843. 4 advance("finally");
6844. 4 the_try.else = block();
6845. 4 the_disrupt = the_try.else.disrupt;
6846. 4 functionage.finally -= 1;
6847. 56 }
6848. 56 the_try.disrupt = the_disrupt;
6849. 56 functionage.try -= 1;
6850. 56 return the_try;
6851. 56 }
6852. 629
6853. 2328 function stmt_var() {
6854. 2328 let ellipsis;
6855. 2328 let mode_const;
6856. 2328 let name;
6857. 2328 let the_brace;
6858. 2328 let the_bracket;
6859. 2328 let the_variable = token_now;
6860. 2328 let variable_prv;
6861. 2328 mode_const = the_variable.id === "const";
6862. 2328 the_variable.names = [];
6863. 2328
6864. 2328// A program may use var or let, but not both.
6865. 2328
6866. 2052 if (!mode_const) {
6867. 2052 if (mode_var === undefined) {
6868. 2052 mode_var = the_variable.id;
6869. 2052 } else if (the_variable.id !== mode_var) {
6870. 2052
6871. 2052// test_cause:
6872. 2052// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
6873. 2052
6874. 2052 warn("expected_a_b", the_variable, mode_var, the_variable.id);
6875. 2052 }
6876. 2052 }
6877. 2328
6878. 2328// We don't expect to see variables created in switch statements.
6879. 2328
6880. 1 if (functionage.switch > 0) {
6881. 1
6882. 1// test_cause:
6883. 1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
6884. 1
6885. 1 warn("var_switch", the_variable);
6886. 1 }
6887. 2328 switch (
6888. 2328 Boolean(functionage.statement_prv)
6889. 1449 && functionage.statement_prv.id
6890. 2328 ) {
6891. 107 case "const":
6892. 1437 case "let":
6893. 1439 case "var":
6894. 1439
6895. 1439// test_cause:
6896. 1439// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0]
6897. 1439// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0]
6898. 1439// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0]
6899. 1439
6900. 1439 test_cause("var_prv", functionage.statement_prv.id);
6901. 1439 variable_prv = functionage.statement_prv;
6902. 1439 break;
6903. 3 case "import":
6904. 3
6905. 3// test_cause:
6906. 3// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0]
6907. 3
6908. 3 test_cause("import_prv");
6909. 3 break;
6910. 879 case false:
6911. 879 break;
6912. 7 default:
6913. 7 if (
6914. 7 (option_dict.beta && !option_dict.variable)
6915. 7 || the_variable.id === "var"
6916. 7 ) {
6917. 7
6918. 7// test_cause:
6919. 7// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15]
6920. 7// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15]
6921. 7// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21]
6922. 7// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10]
6923. 7
6924. 7 warn("var_on_top", token_now);
6925. 7 }
6926. 2328 }
6927. 2329 while (true) {
6928. 2329 if (token_nxt.id === "{") {
6929. 2329 if (the_variable.id === "var") {
6930. 2329
6931. 2329// test_cause:
6932. 2329// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
6933. 2329
6934. 2329 warn("unexpected_a", the_variable);
6935. 2329 }
6936. 2329 the_brace = token_nxt;
6937. 2329 advance("{");
6938. 2329 while (true) {
6939. 2329 name = token_nxt;
6940. 2329 if (!name.identifier) {
6941. 2329
6942. 2329// test_cause:
6943. 2329// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6]
6944. 2329
6945. 2329 return stop("expected_identifier_a");
6946. 2329 }
6947. 2329 survey(name);
6948. 2329 advance();
6949. 2329 if (token_nxt.id === ":") {
6950. 2329 advance(":");
6951. 2329 if (!token_nxt.identifier) {
6952. 2329
6953. 2329// test_cause:
6954. 2329// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9]
6955. 2329// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9]
6956. 2329
6957. 2329 return stop("expected_identifier_a");
6958. 2329 }
6959. 2329
6960. 2329// PR-363 - Bugfix
6961. 2329// Add test against false-warning <uninitialized 'bb'> in code
6962. 2329// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
6963. 2329//
6964. 2329// token_nxt.label = name;
6965. 2329// the_variable.names.push(token_nxt);
6966. 2329// enroll(token_nxt, "variable", mode_const);
6967. 2329
6968. 2329 name = token_nxt;
6969. 2329 the_variable.names.push(name);
6970. 2329 survey(name);
6971. 2329 enroll(name, "variable", mode_const);
6972. 2329
6973. 2329 advance();
6974. 2329 the_brace.open = true;
6975. 2329 } else {
6976. 2329 the_variable.names.push(name);
6977. 2329 enroll(name, "variable", mode_const);
6978. 2329 }
6979. 2329 name.dead = false;
6980. 2329 name.init = true;
6981. 2329 if (token_nxt.id === "=") {
6982. 2329
6983. 2329// test_cause:
6984. 2329// ["let {aa=0}", "stmt_var", "assign", "", 0]
6985. 2329
6986. 2329 test_cause("assign");
6987. 2329 advance("=");
6988. 2329 name.expression = parse_expression();
6989. 2329 the_brace.open = true;
6990. 2329 }
6991. 2329 if (token_nxt.id !== ",") {
6992. 2329 break;
6993. 2329 }
6994. 2329 advance(",");
6995. 2329 }
6996. 2329
6997. 2329// test_cause:
6998. 2329// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
6999. 2329
7000. 2329 check_ordered(the_variable.id, the_variable.names);
7001. 2329 advance("}");
7002. 2329 advance("=");
7003. 2329 the_variable.expression = parse_expression(0);
7004. 2329 } else if (token_nxt.id === "[") {
7005. 2329 if (the_variable.id === "var") {
7006. 2329
7007. 2329// test_cause:
7008. 2329// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
7009. 2329
7010. 2329 warn("unexpected_a", the_variable);
7011. 2329 }
7012. 2329 the_bracket = token_nxt;
7013. 2329 advance("[");
7014. 2329 while (true) {
7015. 2329 ellipsis = false;
7016. 2329 if (token_nxt.id === "...") {
7017. 2329 ellipsis = true;
7018. 2329 advance("...");
7019. 2329 }
7020. 2329 if (!token_nxt.identifier) {
7021. 2329
7022. 2329// test_cause:
7023. 2329// ["let[]", "stmt_var", "expected_identifier_a", "]", 5]
7024. 2329
7025. 2329 return stop("expected_identifier_a");
7026. 2329 }
7027. 2329 name = token_nxt;
7028. 2329 advance();
7029. 2329 the_variable.names.push(name);
7030. 2329 enroll(name, "variable", mode_const);
7031. 2329 name.dead = false;
7032. 2329 name.init = true;
7033. 2329 if (ellipsis) {
7034. 2329 name.ellipsis = true;
7035. 2329 break;
7036. 2329 }
7037. 2329 if (token_nxt.id === "=") {
7038. 2329 advance("=");
7039. 2329 name.expression = parse_expression();
7040. 2329 the_bracket.open = true;
7041. 2329 }
7042. 2329 if (token_nxt.id !== ",") {
7043. 2329 break;
7044. 2329 }
7045. 2329 advance(",");
7046. 2329 }
7047. 2329 advance("]");
7048. 2329 advance("=");
7049. 2329 the_variable.expression = parse_expression(0);
7050. 2329 } else if (token_nxt.identifier) {
7051. 2329 name = token_nxt;
7052. 2329 advance();
7053. 2329 if (name.id === "ignore") {
7054. 2329
7055. 2329// test_cause:
7056. 2329// ["
7057. 2329// let ignore;function aa(ignore) {}
7058. 2329// ", "stmt_var", "unexpected_a", "ignore", 5]
7059. 2329
7060. 2329 warn("unexpected_a", name);
7061. 2329 }
7062. 2329 enroll(name, "variable", mode_const);
7063. 2329 if (token_nxt.id === "=" || mode_const) {
7064. 2329 advance("=");
7065. 2329 name.dead = false;
7066. 2329 name.init = true;
7067. 2329 name.expression = parse_expression(0);
7068. 2329 }
7069. 2329 the_variable.names.push(name);
7070. 2329 } else {
7071. 2329
7072. 2329// test_cause:
7073. 2329// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
7074. 2329// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8]
7075. 2329
7076. 2329 return stop("expected_identifier_a");
7077. 2329 }
7078. 2329 if (token_nxt.id !== ",") {
7079. 2329 break;
7080. 2329 }
7081. 2329
7082. 2329// test_cause:
7083. 2329// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7]
7084. 2329
7085. 2329 warn("expected_a_b", token_nxt, ";", ",");
7086. 2329 advance(",");
7087. 2329 }
7088. 2314
7089. 2314// Warn if variable declarations are unordered.
7090. 2314
7091. 2314 if (
7092. 2314 option_dict.beta
7093. 2314 && !option_dict.unordered
7094. 2309 && !option_dict.variable
7095. 2303 && variable_prv
7096. 1437 && (
7097. 1437 variable_prv.id + " " + variable_prv.names[0].id
7098. 1437 > the_variable.id + " " + the_variable.names[0].id
7099. 1437 )
7100. 3 ) {
7101. 3
7102. 3// test_cause:
7103. 3// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12]
7104. 3// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
7105. 3// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
7106. 3
7107. 3 warn(
7108. 3 "expected_a_b_before_c_d",
7109. 3 the_variable,
7110. 3 the_variable.id,
7111. 3 the_variable.names[0].id,
7112. 3 variable_prv.id,
7113. 3 variable_prv.names[0].id
7114. 3 );
7115. 2314 }
7116. 2314 semicolon();
7117. 2314 return the_variable;
7118. 2314 }
7119. 629
7120. 208 function stmt_while() {
7121. 208 const the_while = token_now;
7122. 208 check_not_top_level(the_while);
7123. 208 functionage.loop += 1;
7124. 208 the_while.expression = condition();
7125. 208 the_while.block = block();
7126. 1 if (the_while.block.disrupt === true) {
7127. 1
7128. 1// test_cause:
7129. 1// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15]
7130. 1
7131. 1 warn("weird_loop", the_while);
7132. 205 }
7133. 205 functionage.loop -= 1;
7134. 205 return the_while;
7135. 205 }
7136. 629
7137. 1 function stmt_with() {
7138. 1
7139. 1// test_cause:
7140. 1// ["with", "stmt_with", "unexpected_a", "with", 1]
7141. 1
7142. 1 stop("unexpected_a", token_now);
7143. 1 }
7144. 629
7145. 14571 function survey(name) {
7146. 14571 let id = name.id;
7147. 14571
7148. 14571// Tally the property name. If it is a string, only tally strings that conform
7149. 14571// to the identifier rules.
7150. 14571
7151. 183 if (id === "(string)") {
7152. 183 id = name.value;
7153. 183 if (!jslint_rgx_identifier.test(id)) {
7154. 183 return id;
7155. 183 }
7156. 14388 } else if (id === "`") {
7157. 14388 if (name.value.length === 1) {
7158. 14388 id = name.value[0].value;
7159. 14388 if (!jslint_rgx_identifier.test(id)) {
7160. 14388 return id;
7161. 14388 }
7162. 14388 }
7163. 14388 } else if (!name.identifier) {
7164. 14388
7165. 14388// test_cause:
7166. 14388// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9]
7167. 14388
7168. 14388 return stop("expected_identifier_a", name);
7169. 14484 }
7170. 14484
7171. 14484// If we have seen this name before, increment its count.
7172. 14484
7173. 14484 if (typeof property_dict[id] === "number") {
7174. 12064 property_dict[id] += 1;
7175. 12064
7176. 12064// If this is the first time seeing this property name, and if there is a
7177. 12064// tenure list, then it must be on the list. Otherwise, it must conform to
7178. 12064// the rules for good property names.
7179. 12064
7180. 12064 } else {
7181. 2420 if (state.mode_property) {
7182. 2420 if (tenure[id] !== true) {
7183. 2420
7184. 2420// test_cause:
7185. 2420// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
7186. 2420
7187. 2420 warn("unregistered_property_a", name);
7188. 2420 }
7189. 2420 } else if (
7190. 2420 !option_dict.nomen
7191. 2420 && name.identifier
7192. 2420 && jslint_rgx_weird_property.test(id)
7193. 2420 ) {
7194. 2420
7195. 2420// test_cause:
7196. 2420// ["aa.$", "survey", "weird_property_a", "$", 4]
7197. 2420// ["aa._", "survey", "weird_property_a", "_", 4]
7198. 2420// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
7199. 2420// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
7200. 2420// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
7201. 2420
7202. 2420 warn("weird_property_a", name);
7203. 2420 }
7204. 2420 property_dict[id] = 1;
7205. 14484 }
7206. 14484 return id;
7207. 14484 }
7208. 629
7209. 629// These functions are used to specify the grammar of our language:
7210. 629
7211. 81770 function symbol(id, bp) {
7212. 81770
7213. 81770// Create a symbol if it does not already exist in the language's syntax.
7214. 81770
7215. 81770 let the_symbol = syntax_dict[id];
7216. 71077 if (the_symbol === undefined) {
7217. 71077 the_symbol = empty();
7218. 71077 the_symbol.id = id;
7219. 71077 the_symbol.lbp = bp || 0;
7220. 71077 syntax_dict[id] = the_symbol;
7221. 71077 }
7222. 81770 return the_symbol;
7223. 81770 }
7224. 629
7225. 629 function ternary(id1, id2) {
7226. 629
7227. 629// Create a ternary operator.
7228. 629
7229. 629 const the_symbol = symbol(id1, 30);
7230. 219 the_symbol.led_infix = function parse_ternary_led(left) {
7231. 219 const the_token = token_now;
7232. 219 let second;
7233. 219 second = parse_expression(20);
7234. 219 advance(id2);
7235. 219 token_now.arity = "ternary";
7236. 219 the_token.arity = "ternary";
7237. 219 the_token.expression = [left, second, parse_expression(10)];
7238. 5 if (token_nxt.id !== ")") {
7239. 5
7240. 5// test_cause:
7241. 5// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
7242. 5
7243. 5 warn("use_open", the_token);
7244. 5 }
7245. 219 return the_token;
7246. 219 };
7247. 629 return the_symbol;
7248. 629 }
7249. 629
7250. 629// Now we parse JavaScript.
7251. 629// Begin defining the language.
7252. 629
7253. 629 assignment("%=");
7254. 629 assignment("&=");
7255. 629 assignment("*=");
7256. 629 assignment("+=");
7257. 629 assignment("-=");
7258. 629 assignment("/=");
7259. 629 assignment("<<=");
7260. 629 assignment("=");
7261. 629 assignment(">>=");
7262. 629 assignment(">>>=");
7263. 629 assignment("^=");
7264. 629 assignment("|=");
7265. 629 constant("(number)", "number");
7266. 629 constant("(regexp)", "regexp");
7267. 629 constant("(string)", "string");
7268. 629 constant("Function", "function", constant_Function);
7269. 629 constant("Infinity", "number", Infinity);
7270. 629 constant("NaN", "number", NaN);
7271. 629 constant("arguments", "object", constant_arguments);
7272. 629 constant("eval", "function", constant_eval);
7273. 629 constant("false", "boolean", false);
7274. 629 constant("ignore", "undefined", constant_ignore);
7275. 629 constant("isFinite", "function", constant_isInfinite);
7276. 629 constant("isNaN", "function", constant_isNaN);
7277. 629 constant("null", "null", null);
7278. 629 constant("this", "object", constant_this);
7279. 629 constant("true", "boolean", true);
7280. 629 constant("undefined", "undefined");
7281. 629 infix(100, "!=");
7282. 629 infix(100, "!==");
7283. 629 infix(100, "==");
7284. 629 infix(100, "===");
7285. 629 infix(110, "<");
7286. 629 infix(110, "<=");
7287. 629 infix(110, ">");
7288. 629 infix(110, ">=");
7289. 629 infix(110, "in");
7290. 629 infix(110, "instanceof");
7291. 629 infix(120, "<<");
7292. 629 infix(120, ">>");
7293. 629 infix(120, ">>>");
7294. 629 infix(130, "+");
7295. 629 infix(130, "-");
7296. 629 infix(140, "%");
7297. 629 infix(140, "*");
7298. 629 infix(140, "/");
7299. 629 infix(160, "(", infix_lparen);
7300. 629 infix(160, "`", infix_grave);
7301. 629 infix(170, ".", infix_dot);
7302. 629 infix(170, "=>", infix_fart_unwrapped);
7303. 629 infix(170, "?.", infix_option_chain);
7304. 629 infix(170, "[", infix_lbracket);
7305. 629 infix(35, "??");
7306. 629 infix(40, "||");
7307. 629 infix(50, "&&");
7308. 629 infix(70, "|");
7309. 629 infix(80, "^");
7310. 629 infix(90, "&");
7311. 629 infixr(150, "**");
7312. 629 postassign("++");
7313. 629 postassign("--");
7314. 629 preassign("++");
7315. 629 preassign("--");
7316. 629 prefix("!!");
7317. 629 prefix("!");
7318. 629 prefix("(", prefix_lparen);
7319. 629 prefix("+");
7320. 629 prefix("-");
7321. 629 prefix("/=", prefix_assign_divide);
7322. 629 prefix("=>", prefix_fart);
7323. 629 prefix("[", prefix_lbracket);
7324. 629 prefix("`", prefix_tick);
7325. 629 prefix("async", prefix_async);
7326. 629 prefix("await", prefix_await);
7327. 629 prefix("function", prefix_function);
7328. 629 prefix("new", prefix_new);
7329. 629 prefix("typeof");
7330. 629 prefix("void", prefix_void);
7331. 629 prefix("{", prefix_lbrace);
7332. 629 prefix("~");
7333. 629 stmt(";", stmt_semicolon);
7334. 629 stmt("async", prefix_async);
7335. 629 stmt("await", prefix_await);
7336. 629 stmt("break", stmt_break);
7337. 629 stmt("const", stmt_var);
7338. 629 stmt("continue", stmt_continue);
7339. 629 stmt("debugger", stmt_debugger);
7340. 629 stmt("delete", stmt_delete);
7341. 629 stmt("do", stmt_do);
7342. 629 stmt("export", stmt_export);
7343. 629 stmt("for", stmt_for);
7344. 629 stmt("function", prefix_function);
7345. 629 stmt("if", stmt_if);
7346. 629 stmt("import", stmt_import);
7347. 629 stmt("let", stmt_var);
7348. 629 stmt("return", stmt_return);
7349. 629 stmt("switch", stmt_switch);
7350. 629 stmt("throw", stmt_throw);
7351. 629 stmt("try", stmt_try);
7352. 629 stmt("var", stmt_var);
7353. 629 stmt("while", stmt_while);
7354. 629 stmt("with", stmt_with);
7355. 629 stmt("{", stmt_lbrace);
7356. 629 symbol(")");
7357. 629 symbol("*/");
7358. 629 symbol(",");
7359. 629 symbol(":");
7360. 629 symbol(";");
7361. 629 symbol("]");
7362. 629 symbol("async");
7363. 629 symbol("await");
7364. 629 symbol("case");
7365. 629 symbol("catch");
7366. 629 symbol("class");
7367. 629 symbol("default");
7368. 629 symbol("else");
7369. 629 symbol("enum");
7370. 629 symbol("finally");
7371. 629 symbol("implements");
7372. 629 symbol("interface");
7373. 629 symbol("package");
7374. 629 symbol("private");
7375. 629 symbol("protected");
7376. 629 symbol("public");
7377. 629 symbol("static");
7378. 629 symbol("super");
7379. 629 symbol("void");
7380. 629 symbol("yield");
7381. 629 symbol("}");
7382. 629 ternary("?", ":");
7383. 629
7384. 629// Init token_nxt.
7385. 629
7386. 629 advance();
7387. 629
7388. 629// Parsing of JSON is simple:
7389. 629
7390. 25 if (state.mode_json) {
7391. 25 state.token_tree = parse_json();
7392. 25 advance("(end)");
7393. 25 return;
7394. 604 }
7395. 604
7396. 604// Because browsers encourage combining of script files, the first token might
7397. 604// be a semicolon to defend against a missing semicolon in the preceding file.
7398. 604
7399. 604 if (option_dict.browser) {
7400. 5 if (token_nxt.id === ";") {
7401. 5 advance(";");
7402. 5 }
7403. 5
7404. 5// If we are not in a browser, then the file form of strict pragma may be used.
7405. 5
7406. 599 } else if (token_nxt.value === "use strict") {
7407. 599 advance("(string)");
7408. 599 advance(";");
7409. 604 }
7410. 604 state.token_tree = parse_statements();
7411. 604 advance("(end)");
7412. 604
7413. 604// Check global functions are ordered.
7414. 604
7415. 604 check_ordered(
7416. 604 "function",
7417. 2001 function_list.map(function ({
7418. 2001 level,
7419. 2001 name
7420. 2001 }) {
7421. 604 return (level === 1) && name;
7422. 2001 }).filter(function (name) {
7423. 1992 return option_dict.beta && name && name.id;
7424. 2001 })
7425. 604 );
7426. 604}
7427. 1
7428. 516function jslint_phase4_walk(state) {
7429. 516
7430. 516// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
7431. 516// recursive traversal. Each node may be processed on the way down
7432. 516// (preaction) and on the way up (postaction).
7433. 516
7434. 516 let {
7435. 516 artifact,
7436. 516 catch_stack,
7437. 516 function_stack,
7438. 516 global_dict,
7439. 516 is_equal,
7440. 516 is_weird,
7441. 516 option_dict,
7442. 516 syntax_dict,
7443. 516 test_cause,
7444. 516 token_global,
7445. 516 warn
7446. 516 } = state;
7447. 516 let block_stack = []; // The stack of blocks.
7448. 516 let blockage = token_global; // The current block.
7449. 516 let catchage = catch_stack[0]; // The current catch-block.
7450. 516 let functionage = token_global; // The current function.
7451. 516 let postaction;
7452. 516 let postamble;
7453. 516 let posts = empty();
7454. 516 let preaction;
7455. 516 let preamble;
7456. 516 let pres = empty();
7457. 516
7458. 516// The relational operators.
7459. 516
7460. 516 let relationop = object_assign_from_list(empty(), [
7461. 516 "!=", "!==", "<", "<=", "==", "===", ">", ">="
7462. 516 ], true);
7463. 516
7464. 516// Ambulation of the parse tree.
7465. 516
7466. 1032 function action(when) {
7467. 1032
7468. 1032// Produce a function that will register task functions that will be called as
7469. 1032// the tree is traversed.
7470. 1032
7471. 19608 return function (arity, id, task) {
7472. 19608 let a_set = when[arity];
7473. 19608 let i_set;
7474. 19608
7475. 19608// The id parameter is optional. If excluded, the task will be applied to all
7476. 19608// ids.
7477. 19608
7478. 4128 if (typeof id !== "string") {
7479. 4128 task = id;
7480. 4128 id = "(all)";
7481. 4128 }
7482. 19608
7483. 19608// If this arity has no registrations yet, then create a set object to hold
7484. 19608// them.
7485. 19608
7486. 5160 if (a_set === undefined) {
7487. 5160 a_set = empty();
7488. 5160 when[arity] = a_set;
7489. 5160 }
7490. 19608
7491. 19608// If this id has no registrations yet, then create a set array to hold them.
7492. 19608
7493. 19608 i_set = a_set[id];
7494. 19092 if (i_set === undefined) {
7495. 19092 i_set = [];
7496. 19092 a_set[id] = i_set;
7497. 19092 }
7498. 19608
7499. 19608// Register the task with the arity and the id.
7500. 19608
7501. 19608 i_set.push(task);
7502. 19608 };
7503. 1032 }
7504. 516
7505. 1032 function amble(when) {
7506. 1032
7507. 1032// Produce a function that will act on the tasks registered by an action
7508. 1032// function while walking the tree.
7509. 1032
7510. 210640 return function (the_token) {
7511. 210640
7512. 210640// Given a task set that was built by an action function, run all
7513. 210640// relevant tasks on the token.
7514. 210640
7515. 210640 let a_set = when[the_token.arity];
7516. 210640 let i_set;
7517. 210640
7518. 210640// If there are tasks associated with the token's arity...
7519. 210640
7520. 144479 if (a_set !== undefined) {
7521. 144479
7522. 144479// If there are tasks associated with the token's id...
7523. 144479
7524. 144479 i_set = a_set[the_token.id];
7525. 144479 if (i_set !== undefined) {
7526. 144479 i_set.forEach(function (task) {
7527. 144479 task(the_token);
7528. 144479 });
7529. 144479 }
7530. 144479
7531. 144479// If there are tasks for all ids.
7532. 144479
7533. 144479 i_set = a_set["(all)"];
7534. 144479 if (i_set !== undefined) {
7535. 144479 i_set.forEach(function (task) {
7536. 144479 task(the_token);
7537. 144479 });
7538. 144479 }
7539. 144479 }
7540. 210640 };
7541. 1032 }
7542. 516
7543. 2817 function init_variable(name) {
7544. 2817 let the_variable = lookup(name);
7545. 2758 if (!the_variable || the_variable.readonly) {
7546. 61 warn("bad_assignment_a", name);
7547. 61 return;
7548. 2756 }
7549. 2756 the_variable.init = true;
7550. 2756 }
7551. 516
7552. 31465 function lookup(thing) {
7553. 31465 let id = thing.id;
7554. 31465 let the_variable;
7555. 1 if (thing.arity !== "variable") {
7556. 1 return;
7557. 31464 }
7558. 31464
7559. 31464// Look up the variable in the current context.
7560. 31464
7561. 31464 the_variable = functionage.context[id] || catchage.context[id];
7562. 31465
7563. 31465// If it isn't local, search all the other contexts. If there are name
7564. 31465// collisions, take the most recent.
7565. 31465
7566. 24589 if (the_variable && the_variable.role === "label") {
7567. 1
7568. 1// test_cause:
7569. 1// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13]
7570. 1
7571. 1 warn("label_a", thing);
7572. 1 return the_variable;
7573. 31463 }
7574. 31463 if (!the_variable) {
7575. 14775 function_stack.forEach(function ({
7576. 14775 context
7577. 14775 }) {
7578. 6875 if (context[id] && context[id].role !== "label") {
7579. 6875 the_variable = context[id];
7580. 6875 }
7581. 14775 });
7582. 6875
7583. 6875// If it isn't in any of those either, perhaps it is a predefined global.
7584. 6875// If so, add it to the global context.
7585. 6875
7586. 6875 if (!the_variable && global_dict[id] === undefined) {
7587. 6875
7588. 6875// test_cause:
7589. 6875// ["aa", "lookup", "undeclared_a", "aa", 1]
7590. 6875// ["class aa{}", "lookup", "undeclared_a", "aa", 7]
7591. 6875// ["
7592. 6875// let aa=0;try{aa();}catch(bb){bb();}bb();
7593. 6875// ", "lookup", "undeclared_a", "bb", 36]
7594. 6875// ["
7595. 6875// let aa=0;try{aa();}catch(ignore){bb();}
7596. 6875// ", "lookup", "undeclared_a", "bb", 34]
7597. 6875
7598. 6875 warn("undeclared_a", thing);
7599. 6875 return;
7600. 6875 }
7601. 6875 if (!the_variable) {
7602. 6875 the_variable = {
7603. 6875 dead: false,
7604. 6875 id,
7605. 6875 init: true,
7606. 6875 parent: token_global,
7607. 6875 readonly: true,
7608. 6875 role: "variable",
7609. 6875 used: 0
7610. 6875 };
7611. 6875 token_global.context[id] = the_variable;
7612. 6875 }
7613. 6875 the_variable.closure = true;
7614. 6875 functionage.context[id] = the_variable;
7615. 31330 }
7616. 31330 if (
7617. 31330 (
7618. 31330 the_variable.calls === undefined
7619. 31330 || functionage.name === undefined
7620. 4364 || the_variable.calls[functionage.name.id] === undefined
7621. 31465 )
7622. 31145 && the_variable.dead
7623. 3 ) {
7624. 3
7625. 3// test_cause:
7626. 3// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23]
7627. 3
7628. 3 warn("out_of_scope_a", thing);
7629. 31330 }
7630. 31330 return the_variable;
7631. 31330 }
7632. 516
7633. 5008 function post_a(thing) {
7634. 5008
7635. 5008// Assignment using = sets the init property of a variable. No other assignment
7636. 5008// operator can do this. A = token keeps that variable (or array of variables
7637. 5008// in case of destructuring) in its name property.
7638. 5008
7639. 5008 const lvalue = thing.expression[0];
7640. 5008 let right;
7641. 4233 if (thing.id === "=") {
7642. 4233 if (thing.names !== undefined) {
7643. 4233
7644. 4233// test_cause:
7645. 4233// ["if(0){aa=0}", "post_a", "=", "", 0]
7646. 4233
7647. 4233 test_cause("=");
7648. 4233
7649. 4233// Probably deadcode.
7650. 4233// if (Array.isArray(thing.names)) {
7651. 4233// thing.names.forEach(init_variable);
7652. 4233// } else {
7653. 4233// init_variable(thing.names);
7654. 4233// }
7655. 4233
7656. 4233 jslint_assert(
7657. 4233 !Array.isArray(thing.names),
7658. 4233 `Expected !Array.isArray(thing.names).`
7659. 4233 );
7660. 4233 init_variable(thing.names);
7661. 4233 } else {
7662. 4233 if (lvalue.id === "[" || lvalue.id === "{") {
7663. 4233 lvalue.expression.forEach(function (thing) {
7664. 4233 if (thing.variable) {
7665. 4233 thing.variable.init = true;
7666. 4233 }
7667. 4233 });
7668. 4233 } else if (
7669. 4233 lvalue.id === "."
7670. 4233 && thing.expression[1].id === "undefined"
7671. 4233 ) {
7672. 4233
7673. 4233// test_cause:
7674. 4233// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1]
7675. 4233
7676. 4233 warn(
7677. 4233 "expected_a_b",
7678. 4233 lvalue.expression,
7679. 4233 "delete",
7680. 4233 "undefined"
7681. 4233 );
7682. 4233 }
7683. 4233 }
7684. 4233 } else {
7685. 775 if (lvalue.arity === "variable") {
7686. 775 if (!lvalue.variable || lvalue.variable.readonly) {
7687. 775 warn("bad_assignment_a", lvalue);
7688. 775 }
7689. 775 }
7690. 775 right = syntax_dict[thing.expression[1].id];
7691. 775 if (
7692. 775 right !== undefined
7693. 775 && (
7694. 775 right.id === "function"
7695. 775 || right.id === "=>"
7696. 775 || (
7697. 775 right.constant
7698. 775 && right.id !== "(number)"
7699. 775 && (right.id !== "(string)" || thing.id !== "+=")
7700. 775 )
7701. 775 )
7702. 775 ) {
7703. 775
7704. 775// test_cause:
7705. 775// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5]
7706. 775
7707. 775 warn("unexpected_a", thing.expression[1]);
7708. 775 }
7709. 775 }
7710. 5008 }
7711. 516
7712. 706 function post_a_pluseq(thing) {
7713. 706 const right = thing.expression[1];
7714. 396 if (right.constant) {
7715. 396 if (
7716. 396 right.value === ""
7717. 396 || (right.id === "(number)" && right.value === "0")
7718. 396 || right.id === "(boolean)"
7719. 396 || right.id === "null"
7720. 396 || right.id === "undefined"
7721. 396 || Number.isNaN(right.value)
7722. 396 ) {
7723. 396 warn("unexpected_a", right);
7724. 396 }
7725. 396 }
7726. 706 }
7727. 516
7728. 31874 function post_b(thing) {
7729. 31874 let right;
7730. 3950 if (relationop[thing.id]) {
7731. 3950 if (
7732. 3950 is_weird(thing.expression[0])
7733. 3950 || is_weird(thing.expression[1])
7734. 3950 || is_equal(thing.expression[0], thing.expression[1])
7735. 3950 || (
7736. 3950 thing.expression[0].constant === true
7737. 3950 && thing.expression[1].constant === true
7738. 3950 )
7739. 3950 ) {
7740. 3950
7741. 3950// test_cause:
7742. 3950// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5]
7743. 3950
7744. 3950 warn("weird_relation_a", thing);
7745. 3950 }
7746. 3950 }
7747. 2023 if (thing.id === "+") {
7748. 2023 if (!option_dict.convert) {
7749. 2023 if (thing.expression[0].value === "") {
7750. 2023
7751. 2023// test_cause:
7752. 2023// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3]
7753. 2023
7754. 2023 warn("expected_a_b", thing, "String(...)", "\"\" +");
7755. 2023 } else if (thing.expression[1].value === "") {
7756. 2023
7757. 2023// test_cause:
7758. 2023// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2]
7759. 2023
7760. 2023 warn("expected_a_b", thing, "String(...)", "+ \"\"");
7761. 2023 }
7762. 2023 }
7763. 29851 } else if (thing.id === "[") {
7764. 29851 if (thing.expression[0].id === "window") {
7765. 29851
7766. 29851// test_cause:
7767. 29851// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10]
7768. 29851
7769. 29851 warn("weird_expression_a", thing, "window[...]");
7770. 29851 }
7771. 29851 if (thing.expression[0].id === "self") {
7772. 29851
7773. 29851// test_cause:
7774. 29851// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8]
7775. 29851
7776. 29851 warn("weird_expression_a", thing, "self[...]");
7777. 29851 }
7778. 29851 } else if (thing.id === "." || thing.id === "?.") {
7779. 29851 if (thing.expression.id === "RegExp") {
7780. 29851
7781. 29851// test_cause:
7782. 29851// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10]
7783. 29851
7784. 29851 warn("weird_expression_a", thing);
7785. 29851 }
7786. 29851 } else if (thing.id !== "=>" && thing.id !== "(") {
7787. 29851 right = thing.expression[1];
7788. 29851 if (
7789. 29851 (thing.id === "+" || thing.id === "-")
7790. 29851 && right.id === thing.id
7791. 29851 && right.arity === "unary"
7792. 29851 && !right.wrapped
7793. 29851 ) {
7794. 29851
7795. 29851// test_cause:
7796. 29851// ["0- -0", "post_b", "wrap_unary", "-", 4]
7797. 29851
7798. 29851 warn("wrap_unary", right);
7799. 29851 }
7800. 29851 if (
7801. 29851 thing.expression[0].constant === true
7802. 29851 && right.constant === true
7803. 29851 ) {
7804. 29851 thing.constant = true;
7805. 29851 }
7806. 29851 }
7807. 31874 }
7808. 516
7809. 1201 function post_b_and(thing) {
7810. 1201 if (
7811. 1201 is_weird(thing.expression[0])
7812. 1196 || is_equal(thing.expression[0], thing.expression[1])
7813. 1182 || thing.expression[0].constant === true
7814. 1180 || thing.expression[1].constant === true
7815. 21 ) {
7816. 21
7817. 21// test_cause:
7818. 21// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
7819. 21// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
7820. 21// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
7821. 21// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
7822. 21// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
7823. 21// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
7824. 21// ["
7825. 21// aa=function aa(){}&&function aa(){}
7826. 21// ", "post_b_and", "weird_condition_a", "&&", 19]
7827. 21// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
7828. 21
7829. 21 warn("weird_condition_a", thing);
7830. 21 }
7831. 1201 }
7832. 516
7833. 1465 function post_b_lbracket(thing) {
7834. 1 if (thing.expression[0].id === "RegExp") {
7835. 1
7836. 1// test_cause:
7837. 1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
7838. 1
7839. 1 warn("weird_expression_a", thing);
7840. 1 }
7841. 1 if (is_weird(thing.expression[1])) {
7842. 1
7843. 1// test_cause:
7844. 1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
7845. 1
7846. 1 warn("weird_expression_a", thing.expression[1]);
7847. 1 }
7848. 1465 }
7849. 516
7850. 10392 function post_b_lparen(thing) {
7851. 10392 let arg;
7852. 10392 let array;
7853. 10392 let cack;
7854. 10392 let left = thing.expression[0];
7855. 10392 let new_date;
7856. 10392 let paren;
7857. 10392 let the_new;
7858. 154 if (left.id === "new") {
7859. 154 the_new = left;
7860. 154 left = left.expression;
7861. 154 }
7862. 37 if (left.id === "function") {
7863. 37 if (!thing.wrapped) {
7864. 37
7865. 37// test_cause:
7866. 37// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
7867. 37
7868. 37 warn("wrap_immediate", thing);
7869. 37 }
7870. 10355 } else if (left.identifier) {
7871. 10355 if (the_new !== undefined) {
7872. 10355 if (
7873. 10355 left.id[0] > "Z"
7874. 10355 || left.id === "BigInt"
7875. 10355 || left.id === "Boolean"
7876. 10355 || left.id === "Number"
7877. 10355 || left.id === "String"
7878. 10355 || left.id === "Symbol"
7879. 10355 ) {
7880. 10355
7881. 10355// test_cause:
7882. 10355// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
7883. 10355// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
7884. 10355// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
7885. 10355// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
7886. 10355// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
7887. 10355// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
7888. 10355
7889. 10355 warn("unexpected_a", the_new);
7890. 10355 } else if (left.id === "Function") {
7891. 10355 if (!option_dict.eval) {
7892. 10355
7893. 10355// test_cause:
7894. 10355// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5]
7895. 10355
7896. 10355 warn("unexpected_a", left, "new Function");
7897. 10355 }
7898. 10355 } else if (left.id === "Array") {
7899. 10355 arg = thing.expression;
7900. 10355 if (arg.length !== 2 || arg[1].id === "(string)") {
7901. 10355
7902. 10355// test_cause:
7903. 10355// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
7904. 10355
7905. 10355 warn("expected_a_b", left, "[]", "new Array");
7906. 10355 }
7907. 10355 } else if (left.id === "Object") {
7908. 10355
7909. 10355// test_cause:
7910. 10355// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
7911. 10355
7912. 10355 warn(
7913. 10355 "expected_a_b",
7914. 10355 left,
7915. 10355 "Object.create(null)",
7916. 10355 "new Object"
7917. 10355 );
7918. 10355 }
7919. 10355 } else {
7920. 10355 if (
7921. 10355 left.id[0] >= "A"
7922. 10355 && left.id[0] <= "Z"
7923. 10355 && left.id !== "BigInt"
7924. 10355 && left.id !== "Boolean"
7925. 10355 && left.id !== "Number"
7926. 10355 && left.id !== "String"
7927. 10355 && left.id !== "Symbol"
7928. 10355 ) {
7929. 10355
7930. 10355// test_cause:
7931. 10355// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
7932. 10355
7933. 10355 warn("expected_a_before_b", left, "new", artifact(left));
7934. 10355 }
7935. 10355 }
7936. 10355 } else if (left.id === ".") {
7937. 10355 cack = the_new !== undefined;
7938. 10355 if (left.expression.id === "Date" && left.name.id === "UTC") {
7939. 10355
7940. 10355// test_cause:
7941. 10355// ["new Date.UTC()", "post_b_lparen", "cack", "", 0]
7942. 10355
7943. 10355 test_cause("cack");
7944. 10355 cack = !cack;
7945. 10355 }
7946. 10355 if (jslint_rgx_cap.test(left.name.id) !== cack) {
7947. 10355 if (the_new !== undefined) {
7948. 10355
7949. 10355// test_cause:
7950. 10355// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
7951. 10355
7952. 10355 warn("unexpected_a", the_new);
7953. 10355 } else {
7954. 10355
7955. 10355// test_cause:
7956. 10355// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
7957. 10355
7958. 10355 warn(
7959. 10355 "expected_a_before_b",
7960. 10355 left.expression,
7961. 10355 "new",
7962. 10355 left.name.id
7963. 10355 );
7964. 10355 }
7965. 10355 }
7966. 10355 if (left.name.id === "getTime") {
7967. 10355 paren = left.expression;
7968. 10355 if (paren.id === "(") {
7969. 10355 array = paren.expression;
7970. 10355 if (array.length === 1) {
7971. 10355 new_date = array[0];
7972. 10355 if (
7973. 10355 new_date.id === "new"
7974. 10355 && new_date.expression.id === "Date"
7975. 10355 ) {
7976. 10355
7977. 10355// test_cause:
7978. 10355// ["
7979. 10355// new Date().getTime()
7980. 10355// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
7981. 10355
7982. 10355 warn(
7983. 10355 "expected_a_b",
7984. 10355 new_date,
7985. 10355 "Date.now()",
7986. 10355 "new Date().getTime()"
7987. 10355 );
7988. 10355 }
7989. 10355 }
7990. 10355 }
7991. 10355 }
7992. 10355 }
7993. 10392 }
7994. 516
7995. 963 function post_b_or(thing) {
7996. 963 if (
7997. 963 is_weird(thing.expression[0])
7998. 963 || is_equal(thing.expression[0], thing.expression[1])
7999. 962 || thing.expression[0].constant === true
8000. 2 ) {
8001. 2
8002. 2// test_cause:
8003. 2// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
8004. 2
8005. 2 warn("weird_condition_a", thing);
8006. 2 }
8007. 963 }
8008. 516
8009. 78 function post_s_export(the_thing) {
8010. 78
8011. 78// Some features must be at the most outermost level.
8012. 78
8013. 1 if (blockage !== token_global) {
8014. 1
8015. 1// test_cause:
8016. 1// ["
8017. 1// if(0){import aa from "aa";}
8018. 1// ", "post_s_export", "misplaced_a", "import", 7]
8019. 1
8020. 1 warn("misplaced_a", the_thing);
8021. 1 }
8022. 78 }
8023. 516
8024. 8 function post_s_for(thing) {
8025. 8
8026. 8// Recurse walk_statement().
8027. 8
8028. 8 walk_statement(thing.inc);
8029. 8 }
8030. 516
8031. 2001 function post_s_function(thing) {
8032. 2001 delete functionage.async;
8033. 2001 delete functionage.finally;
8034. 2001 delete functionage.loop;
8035. 2001 delete functionage.statement_prv;
8036. 2001 delete functionage.switch;
8037. 2001 delete functionage.try;
8038. 2001 functionage = function_stack.pop();
8039. 3 if (thing.wrapped) {
8040. 3
8041. 3// test_cause:
8042. 3// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
8043. 3
8044. 3 warn("unexpected_parens", thing);
8045. 3 }
8046. 2001 return post_s_lbrace();
8047. 2001 }
8048. 516
8049. 61 function post_s_import(the_thing) {
8050. 61 const name = the_thing.name;
8051. 60 if (name) {
8052. 60 if (Array.isArray(name)) {
8053. 60 name.forEach(function (name) {
8054. 60 name.dead = false;
8055. 60 name.init = true;
8056. 60 blockage.live.push(name);
8057. 60 });
8058. 60 } else {
8059. 60 name.dead = false;
8060. 60 name.init = true;
8061. 60 blockage.live.push(name);
8062. 60 }
8063. 60 return post_s_export(the_thing);
8064. 60 }
8065. 61 }
8066. 516
8067. 7684 function post_s_lbrace() {
8068. 2981 blockage.live.forEach(function (name) {
8069. 2981 name.dead = true;
8070. 2981 });
8071. 7684 delete blockage.live;
8072. 7684 blockage = block_stack.pop();
8073. 7684 }
8074. 516
8075. 56 function post_s_try(thing) {
8076. 54 if (thing.catch) {
8077. 54 if (thing.catch.name) {
8078. 54 Object.assign(catchage.context[thing.catch.name.id], {
8079. 54 dead: false,
8080. 54 init: true
8081. 54 });
8082. 54 }
8083. 54
8084. 54// Recurse walk_statement().
8085. 54
8086. 54 walk_statement(thing.catch.block);
8087. 54
8088. 54// Restore previous catch-scope after catch-block.
8089. 54
8090. 54 catchage = catch_stack.pop();
8091. 54 }
8092. 56 }
8093. 516
8094. 2314 function post_s_var(thing) {
8095. 2629 thing.names.forEach(function (name) {
8096. 2629 name.dead = false;
8097. 1239 if (name.expression !== undefined) {
8098. 1239 walk_expression(name.expression);
8099. 1239
8100. 1239// Probably deadcode.
8101. 1239// if (name.id === "{" || name.id === "[") {
8102. 1239// name.names.forEach(subactivate);
8103. 1239// } else {
8104. 1239// name.init = true;
8105. 1239// }
8106. 1239
8107. 1239 jslint_assert(
8108. 1239 !(name.id === "{" || name.id === "["),
8109. 1239 `Expected !(name.id === "{" || name.id === "[").`
8110. 1239 );
8111. 1239 name.init = true;
8112. 1239 }
8113. 2629 blockage.live.push(name);
8114. 2629 });
8115. 2314 }
8116. 516
8117. 219 function post_t(thing) {
8118. 219 if (
8119. 219 is_weird(thing.expression[0])
8120. 219 || thing.expression[0].constant === true
8121. 212 || is_equal(thing.expression[1], thing.expression[2])
8122. 9 ) {
8123. 9
8124. 9// test_cause:
8125. 9// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11]
8126. 9// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11]
8127. 9
8128. 9 warn("unexpected_a", thing);
8129. 210 } else if (is_equal(thing.expression[0], thing.expression[1])) {
8130. 210
8131. 210// test_cause:
8132. 210// ["aa?aa:0", "post_t", "expected_a_b", "?", 3]
8133. 210
8134. 210 warn("expected_a_b", thing, "||", "?");
8135. 210 } else if (is_equal(thing.expression[0], thing.expression[2])) {
8136. 210
8137. 210// test_cause:
8138. 210// ["aa?0:aa", "post_t", "expected_a_b", "?", 3]
8139. 210
8140. 210 warn("expected_a_b", thing, "&&", "?");
8141. 210 } else if (
8142. 210 thing.expression[1].id === "true"
8143. 210 && thing.expression[2].id === "false"
8144. 210 ) {
8145. 210
8146. 210// test_cause:
8147. 210// ["aa?true:false", "post_t", "expected_a_b", "?", 3]
8148. 210
8149. 210 warn("expected_a_b", thing, "!!", "?");
8150. 210 } else if (
8151. 210 thing.expression[1].id === "false"
8152. 210 && thing.expression[2].id === "true"
8153. 210 ) {
8154. 210
8155. 210// test_cause:
8156. 210// ["aa?false:true", "post_t", "expected_a_b", "?", 3]
8157. 210
8158. 210 warn("expected_a_b", thing, "!", "?");
8159. 210 } else if (
8160. 210 thing.expression[0].wrapped !== true
8161. 210 && (
8162. 210 thing.expression[0].id === "||"
8163. 210 || thing.expression[0].id === "&&"
8164. 210 )
8165. 210 ) {
8166. 210
8167. 210// test_cause:
8168. 210// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4]
8169. 210
8170. 210 warn("wrap_condition", thing.expression[0]);
8171. 210 }
8172. 219 }
8173. 516
8174. 4106 function post_u(thing) {
8175. 779 if (thing.id === "`") {
8176. 779 if (thing.expression.every(function (thing) {
8177. 779 return thing.constant;
8178. 779 })) {
8179. 779 thing.constant = true;
8180. 779 }
8181. 3327 } else if (thing.id === "!") {
8182. 3327 if (thing.expression.constant === true) {
8183. 3327 warn("unexpected_a", thing);
8184. 3327 }
8185. 3327 } else if (thing.id === "!!") {
8186. 3327 if (!option_dict.convert) {
8187. 3327
8188. 3327// test_cause:
8189. 3327// ["!!0", "post_u", "expected_a_b", "!!", 1]
8190. 3327
8191. 3327 warn("expected_a_b", thing, "Boolean(...)", "!!");
8192. 3327 }
8193. 3327 } else if (
8194. 3327 thing.id !== "["
8195. 3327 && thing.id !== "{"
8196. 3327 && thing.id !== "function"
8197. 3327 && thing.id !== "new"
8198. 3327 ) {
8199. 3327 if (thing.expression.constant === true) {
8200. 3327 thing.constant = true;
8201. 3327 }
8202. 3327 }
8203. 4106 }
8204. 516
8205. 7 function post_u_plus(thing) {
8206. 7 const right = thing.expression;
8207. 7 if (!option_dict.convert) {
8208. 7
8209. 7// test_cause:
8210. 7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
8211. 7
8212. 7 warn("expected_a_b", thing, "Number(...)", "+");
8213. 7 }
8214. 1 if (right.id === "(" && right.expression[0].id === "new") {
8215. 1 warn("unexpected_a_before_b", thing, "+", "new");
8216. 6 } else if (
8217. 6 right.constant
8218. 6 || right.id === "{"
8219. 6 || (right.id === "[" && right.arity !== "binary")
8220. 6 ) {
8221. 6 warn("unexpected_a", thing, "+");
8222. 6 }
8223. 7 }
8224. 516
8225. 36885 function pre_a_bitwise(thing) {
8226. 36885
8227. 36885// These are the bitwise operators.
8228. 36885
8229. 36883 switch (!option_dict.bitwise && thing.id) {
8230. 2 case "&":
8231. 3 case "&=":
8232. 5 case "<<":
8233. 6 case "<<=":
8234. 8 case ">>":
8235. 9 case ">>=":
8236. 11 case ">>>":
8237. 12 case ">>>=":
8238. 14 case "^":
8239. 15 case "^=":
8240. 17 case "|":
8241. 18 case "|=":
8242. 21 case "~":
8243. 21
8244. 21// test_cause:
8245. 21// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
8246. 21// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
8247. 21// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
8248. 21// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
8249. 21// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
8250. 21// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
8251. 21// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
8252. 21// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
8253. 21// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
8254. 21// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
8255. 21// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
8256. 21// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
8257. 21// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
8258. 21
8259. 21 warn("unexpected_a", thing);
8260. 21 break;
8261. 36885 }
8262. 36885 if (
8263. 36885 thing.id !== "("
8264. 26493 && thing.id !== "&&"
8265. 25292 && thing.id !== "||"
8266. 24329 && thing.id !== "="
8267. 20096 && Array.isArray(thing.expression)
8268. 8501 && thing.expression.length === 2
8269. 8500 && (
8270. 8500 relationop[thing.expression[0].id] === true
8271. 8500 || relationop[thing.expression[1].id] === true
8272. 8500 )
8273. 1 ) {
8274. 1
8275. 1// test_cause:
8276. 1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
8277. 1
8278. 1 warn("unexpected_a", thing);
8279. 1 }
8280. 36885 }
8281. 516
8282. 31874 function pre_b(thing) {
8283. 31874 let left;
8284. 31874 let right;
8285. 31874 let value;
8286. 3950 if (relationop[thing.id] === true) {
8287. 3950 left = thing.expression[0];
8288. 3950 right = thing.expression[1];
8289. 3950 if (left.id === "NaN" || right.id === "NaN") {
8290. 3950
8291. 3950// test_cause:
8292. 3950// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4]
8293. 3950
8294. 3950 warn("number_isNaN", thing);
8295. 3950 } else if (left.id === "typeof") {
8296. 3950 if (right.id !== "(string)") {
8297. 3950 if (right.id !== "typeof") {
8298. 3950
8299. 3950// test_cause:
8300. 3950// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12]
8301. 3950
8302. 3950 warn("expected_string_a", right);
8303. 3950 }
8304. 3950 } else {
8305. 3950 value = right.value;
8306. 3950 if (value === "null" || value === "undefined") {
8307. 3950
8308. 3950// test_cause:
8309. 3950// ["
8310. 3950// typeof aa==="undefined"
8311. 3950// ", "pre_b", "unexpected_typeof_a", "undefined", 13]
8312. 3950
8313. 3950 warn("unexpected_typeof_a", right, value);
8314. 3950 } else if (
8315. 3950 value !== "bigint"
8316. 3950 && value !== "boolean"
8317. 3950 && value !== "function"
8318. 3950 && value !== "number"
8319. 3950 && value !== "object"
8320. 3950 && value !== "string"
8321. 3950 && value !== "symbol"
8322. 3950 ) {
8323. 3950
8324. 3950// test_cause:
8325. 3950// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12]
8326. 3950
8327. 3950 warn("expected_type_string_a", right, value);
8328. 3950 }
8329. 3950 }
8330. 3950 }
8331. 3950 }
8332. 31874 }
8333. 516
8334. 1 function pre_b_eqeq(thing) {
8335. 1
8336. 1// test_cause:
8337. 1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
8338. 1
8339. 1 warn("expected_a_b", thing, "===", "==");
8340. 1 }
8341. 516
8342. 1 function pre_b_in(thing) {
8343. 1
8344. 1// test_cause:
8345. 1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
8346. 1
8347. 1 warn("infix_in", thing);
8348. 1 }
8349. 516
8350. 1 function pre_b_instanceof(thing) {
8351. 1
8352. 1// test_cause:
8353. 1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
8354. 1
8355. 1 warn("unexpected_a", thing);
8356. 1 }
8357. 516
8358. 10392 function pre_b_lparen(thing) {
8359. 10392 const left = thing.expression[0];
8360. 10392 let left_variable;
8361. 10392 let parent;
8362. 10392 if (
8363. 10392 left.identifier
8364. 6652 && functionage.context[left.id] === undefined
8365. 3033 && typeof functionage.name === "object"
8366. 2395 ) {
8367. 2395 parent = functionage.name.parent;
8368. 2395 if (parent) {
8369. 2395 left_variable = parent.context[left.id];
8370. 2395 if (
8371. 2395 left_variable !== undefined
8372. 2395
8373. 2395// Probably deadcode.
8374. 2395// && left_variable.dead
8375. 2395
8376. 2395 && left_variable.parent === parent
8377. 2395 && left_variable.calls !== undefined
8378. 2395 && left_variable.calls[functionage.name.id] !== undefined
8379. 2395 ) {
8380. 2395 left_variable.dead = false;
8381. 2395 }
8382. 2395 }
8383. 2395 }
8384. 10392 }
8385. 516
8386. 1 function pre_b_noteq(thing) {
8387. 1
8388. 1// test_cause:
8389. 1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
8390. 1
8391. 1 warn("expected_a_b", thing, "!==", "!=");
8392. 1 }
8393. 516
8394. 963 function pre_b_or(thing) {
8395. 1926 thing.expression.forEach(function (thang) {
8396. 177 if (thang.id === "&&" && !thang.wrapped) {
8397. 1
8398. 1// test_cause:
8399. 1// ["0&&0||0", "pre_b_or", "and", "&&", 2]
8400. 1
8401. 1 warn("and", thang);
8402. 1 }
8403. 1926 });
8404. 963 }
8405. 516
8406. 8 function pre_s_for(thing) {
8407. 8 let the_variable;
8408. 2 if (thing.name !== undefined) {
8409. 2 thing.name.dead = false;
8410. 2 the_variable = lookup(thing.name);
8411. 2 if (the_variable !== undefined) {
8412. 2 if (the_variable.init && the_variable.readonly) {
8413. 2
8414. 2// test_cause:
8415. 2// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16]
8416. 2
8417. 2 warn("bad_assignment_a", thing.name);
8418. 2 }
8419. 2 the_variable.init = true;
8420. 2 }
8421. 2 }
8422. 8
8423. 8// Recurse walk_statement().
8424. 8
8425. 8 walk_statement(thing.initial);
8426. 8 }
8427. 516
8428. 2001 function pre_s_function(thing) {
8429. 2001
8430. 2001// test_cause:
8431. 2001// ["()=>0", "pre_s_function", "", "", 0]
8432. 2001// ["(function (){}())", "pre_s_function", "", "", 0]
8433. 2001// ["function aa(){}", "pre_s_function", "", "", 0]
8434. 2001
8435. 2001 test_cause("");
8436. 1057 if (thing.arity === "statement" && blockage.body !== true) {
8437. 1
8438. 1// test_cause:
8439. 1// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7]
8440. 1
8441. 1 warn("unexpected_a", thing);
8442. 1 }
8443. 2001 function_stack.push(functionage);
8444. 2001 block_stack.push(blockage);
8445. 2001 functionage = thing;
8446. 2001 blockage = thing;
8447. 2001 thing.live = [];
8448. 1098 if (typeof thing.name === "object") {
8449. 1098 thing.name.dead = false;
8450. 1098 thing.name.init = true;
8451. 1098 }
8452. 7 if (thing.extra === "get") {
8453. 7 if (thing.parameters.length !== 0) {
8454. 7
8455. 7// test_cause:
8456. 7// ["
8457. 7// /*jslint getset*/
8458. 7// aa={get aa(aa){}}
8459. 7// ", "pre_s_function", "bad_get", "function", 9]
8460. 7
8461. 7 warn("bad_get", thing);
8462. 7 }
8463. 1994 } else if (thing.extra === "set") {
8464. 1994 if (thing.parameters.length !== 1) {
8465. 1994
8466. 1994// test_cause:
8467. 1994// ["
8468. 1994// /*jslint getset*/
8469. 1994// aa={set aa(){}}
8470. 1994// ", "pre_s_function", "bad_set", "function", 9]
8471. 1994
8472. 1994 warn("bad_set", thing);
8473. 1994 }
8474. 1994 }
8475. 2063 thing.parameters.forEach(function (name) {
8476. 2063 walk_expression(name.expression);
8477. 1840 if (name.id === "{" || name.id === "[") {
8478. 267 name.names.forEach(subactivate);
8479. 1796 } else {
8480. 1796 name.dead = false;
8481. 1796 name.init = true;
8482. 1796 }
8483. 2063 });
8484. 2001 }
8485. 516
8486. 5683 function pre_s_lbrace(thing) {
8487. 5683 block_stack.push(blockage);
8488. 5683 blockage = thing;
8489. 5683 thing.live = [];
8490. 5683 }
8491. 516
8492. 56 function pre_try(thing) {
8493. 54 if (thing.catch !== undefined) {
8494. 54
8495. 54// Create new catch-scope for catch-parameter.
8496. 54
8497. 54 catch_stack.push(catchage);
8498. 54 catchage = thing.catch;
8499. 54 }
8500. 56 }
8501. 516
8502. 28646 function pre_v(thing) {
8503. 28646 const the_variable = lookup(thing);
8504. 28572 if (the_variable !== undefined) {
8505. 28572 thing.variable = the_variable;
8506. 28572 the_variable.used += 1;
8507. 28572 }
8508. 28646 }
8509. 516
8510. 717 function subactivate(name) {
8511. 717 name.init = true;
8512. 717 name.dead = false;
8513. 717 blockage.live.push(name);
8514. 717 }
8515. 516
8516. 164313 function walk_expression(thing) {
8517. 103647 if (thing) {
8518. 103647 if (Array.isArray(thing)) {
8519. 103647
8520. 103647// test_cause:
8521. 103647// ["(function(){}())", "walk_expression", "isArray", "", 0]
8522. 103647// ["0&&0", "walk_expression", "isArray", "", 0]
8523. 103647
8524. 103647 test_cause("isArray");
8525. 103647 thing.forEach(walk_expression);
8526. 103647 } else {
8527. 103647 preamble(thing);
8528. 103647 walk_expression(thing.expression);
8529. 103647
8530. 103647// PR-414 - Bugfix - fix fart-body not being walked.
8531. 103647
8532. 103647 if (thing.id === "function" || thing.id === "=>") {
8533. 103647
8534. 103647// test_cause:
8535. 103647// ["aa=()=>0", "walk_expression", "function", "=>", 0]
8536. 103647// ["aa=function(){}", "walk_expression", "function", "function", 0]
8537. 103647
8538. 103647 test_cause("function", thing.id);
8539. 103647
8540. 103647// Recurse walk_statement().
8541. 103647
8542. 103647 walk_statement(thing.block);
8543. 103647 }
8544. 103647 if (
8545. 103647 thing.arity === "preassign" || thing.arity === "postassign"
8546. 103647 ) {
8547. 103647
8548. 103647// test_cause:
8549. 103647// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4]
8550. 103647// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4]
8551. 103647
8552. 103647 warn("unexpected_a", thing);
8553. 103647 } else if (
8554. 103647 thing.arity === "statement"
8555. 103647 || thing.arity === "assignment"
8556. 103647 ) {
8557. 103647
8558. 103647// test_cause:
8559. 103647// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
8560. 103647
8561. 103647 warn("unexpected_statement_a", thing);
8562. 103647 }
8563. 103647
8564. 103647// test_cause:
8565. 103647// ["aa=0", "walk_expression", "default", "", 0]
8566. 103647
8567. 103647 test_cause("default");
8568. 103647 postamble(thing);
8569. 103647 }
8570. 103647 }
8571. 164313 }
8572. 516
8573. 78189 function walk_statement(thing) {
8574. 43035 if (!thing) {
8575. 43035 return;
8576. 43035 }
8577. 35154 if (Array.isArray(thing)) {
8578. 7595
8579. 7595// test_cause:
8580. 7595// ["+[]", "walk_statement", "isArray", "", 0]
8581. 7595
8582. 7595 test_cause("isArray");
8583. 7595
8584. 7595// Recurse walk_statement().
8585. 7595
8586. 7595 thing.forEach(walk_statement);
8587. 7595 return;
8588. 27559 }
8589. 27559 preamble(thing);
8590. 27559 walk_expression(thing.expression);
8591. 27559 if (thing.arity === "binary") {
8592. 5658 if (thing.id !== "(") {
8593. 5658
8594. 5658// test_cause:
8595. 5658// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
8596. 5658
8597. 5658 warn("unexpected_expression_a", thing);
8598. 5658 }
8599. 21901 } else if (
8600. 21901 thing.arity !== "statement"
8601. 21901 && thing.arity !== "assignment"
8602. 21901 && thing.id !== "import"
8603. 21901 ) {
8604. 21901
8605. 21901// test_cause:
8606. 21901// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
8607. 21901// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
8608. 21901// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
8609. 21901// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
8610. 21901// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
8611. 21901
8612. 21901 warn("unexpected_expression_a", thing);
8613. 27559 }
8614. 27559
8615. 27559// Recurse walk_statement().
8616. 27559
8617. 27559 walk_statement(thing.block);
8618. 27559 walk_statement(thing.else);
8619. 27559 postamble(thing);
8620. 27559 }
8621. 516
8622. 516 postaction = action(posts);
8623. 516 postamble = amble(posts);
8624. 516 preaction = action(pres);
8625. 516 preamble = amble(pres);
8626. 516 postaction("assignment", "+=", post_a_pluseq);
8627. 516 postaction("assignment", post_a);
8628. 516 postaction("binary", "&&", post_b_and);
8629. 516 postaction("binary", "(", post_b_lparen);
8630. 516 postaction("binary", "=>", post_s_function);
8631. 516 postaction("binary", "[", post_b_lbracket);
8632. 516 postaction("binary", "||", post_b_or);
8633. 516 postaction("binary", post_b);
8634. 516 postaction("statement", "const", post_s_var);
8635. 516 postaction("statement", "export", post_s_export);
8636. 516 postaction("statement", "for", post_s_for);
8637. 516 postaction("statement", "function", post_s_function);
8638. 516 postaction("statement", "import", post_s_import);
8639. 516 postaction("statement", "let", post_s_var);
8640. 516 postaction("statement", "try", post_s_try);
8641. 516 postaction("statement", "var", post_s_var);
8642. 516 postaction("statement", "{", post_s_lbrace);
8643. 516 postaction("ternary", post_t);
8644. 516 postaction("unary", "+", post_u_plus);
8645. 516 postaction("unary", "function", post_s_function);
8646. 516 postaction("unary", post_u);
8647. 516 preaction("assignment", pre_a_bitwise);
8648. 516 preaction("binary", "!=", pre_b_noteq);
8649. 516 preaction("binary", "(", pre_b_lparen);
8650. 516 preaction("binary", "==", pre_b_eqeq);
8651. 516 preaction("binary", "=>", pre_s_function);
8652. 516 preaction("binary", "in", pre_b_in);
8653. 516 preaction("binary", "instanceof", pre_b_instanceof);
8654. 516 preaction("binary", "||", pre_b_or);
8655. 516 preaction("binary", pre_b);
8656. 516 preaction("binary", pre_a_bitwise);
8657. 516 preaction("statement", "for", pre_s_for);
8658. 516 preaction("statement", "function", pre_s_function);
8659. 516 preaction("statement", "try", pre_try);
8660. 516 preaction("statement", "{", pre_s_lbrace);
8661. 516 preaction("unary", "function", pre_s_function);
8662. 516 preaction("unary", "~", pre_a_bitwise);
8663. 516 preaction("variable", pre_v);
8664. 516
8665. 516 walk_statement(state.token_tree);
8666. 516}
8667. 1
8668. 208function jslint_phase5_whitage(state) {
8669. 208
8670. 208// PHASE 5. Check whitespace between tokens in <token_list>.
8671. 208
8672. 208 let {
8673. 208 artifact,
8674. 208 catch_list,
8675. 208 function_list,
8676. 208 function_stack,
8677. 208 option_dict,
8678. 208 test_cause,
8679. 208 token_global,
8680. 208 token_list,
8681. 208 warn
8682. 208 } = state;
8683. 208 let closer = "(end)";
8684. 208 let free = false;
8685. 208
8686. 208// free = false
8687. 208
8688. 208// cause:
8689. 208// "()=>0"
8690. 208// "aa()"
8691. 208// "aa(0,0)"
8692. 208// "function(){}"
8693. 208
8694. 208// free = true
8695. 208
8696. 208// cause:
8697. 208// "(0)"
8698. 208// "(aa)"
8699. 208// "aa(0)"
8700. 208// "do{}while()"
8701. 208// "for(){}"
8702. 208// "if(){}"
8703. 208// "switch(){}"
8704. 208// "while(){}"
8705. 208
8706. 208 let left = token_global;
8707. 208 let margin = 0;
8708. 208 let mode_indent = (
8709. 208
8710. 208// PR-330 - Allow 2-space indent.
8711. 208
8712. 208 option_dict.indent2
8713. 5 ? 2
8714. 203 : 4
8715. 208 );
8716. 208 let nr_comments_skipped = 0;
8717. 208 let open = true;
8718. 208 let opening = true;
8719. 208 let right;
8720. 208
8721. 208// This is the set of infix operators that require a space on each side.
8722. 208
8723. 208 let spaceop = object_assign_from_list(empty(), [
8724. 208 "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
8725. 208 "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
8726. 208 ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
8727. 208 ], true);
8728. 208
8729. 38041 function at_margin(fit) {
8730. 38041 const at = margin + fit;
8731. 21 if (right.from !== at) {
8732. 21 return expected_at(at);
8733. 21 }
8734. 38041 }
8735. 208
8736. 2057 function delve(the_function) {
8737. 13008 Object.keys(the_function.context).forEach(function (id) {
8738. 13008 const name = the_function.context[id];
8739. 12973 if (id !== "ignore" && name.parent === the_function) {
8740. 6280
8741. 6280// test_cause:
8742. 6280// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
8743. 6280
8744. 6280 test_cause("id");
8745. 6280 if (
8746. 6280 name.used === 0
8747. 6280
8748. 6280// Probably deadcode.
8749. 6280// && (
8750. 6280// name.role !== "function"
8751. 6280// || name.parent.arity !== "unary"
8752. 6280// )
8753. 6280
8754. 6280 && jslint_assert(
8755. 6280 name.role !== "function",
8756. 6280 `Expected name.role !== "function".`
8757. 6280 )
8758. 6280 ) {
8759. 6280
8760. 6280// test_cause:
8761. 6280// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5]
8762. 6280// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13]
8763. 6280// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
8764. 6280
8765. 6280 warn("unused_a", name);
8766. 6280 } else if (!name.init) {
8767. 6280
8768. 6280// test_cause:
8769. 6280// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5]
8770. 6280
8771. 6280 warn("uninitialized_a", name);
8772. 6280 }
8773. 6280 }
8774. 13008 });
8775. 2057 }
8776. 208
8777. 25 function expected_at(at) {
8778. 25
8779. 25// Probably deadcode.
8780. 25// if (right === undefined) {
8781. 25// right = token_nxt;
8782. 25// }
8783. 25
8784. 25 jslint_assert(
8785. 25 !(right === undefined),
8786. 25 `Expected !(right === undefined).`
8787. 25 );
8788. 25 warn(
8789. 25 "expected_a_at_b_c",
8790. 25 right,
8791. 25 artifact(right),
8792. 25
8793. 25// Fudge column numbers in warning message.
8794. 25
8795. 25 at + jslint_fudge,
8796. 25 right.from + jslint_fudge
8797. 25 );
8798. 25 }
8799. 208
8800. 3094 function no_space() {
8801. 3093 if (left.line === right.line) {
8802. 3093
8803. 3093// from:
8804. 3093// if (left.line === right.line) {
8805. 3093// no_space();
8806. 3093// } else {
8807. 3093
8808. 3093 if (left.thru !== right.from && nr_comments_skipped === 0) {
8809. 3093
8810. 3093// test_cause:
8811. 3093// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
8812. 3093
8813. 3093 warn(
8814. 3093 "unexpected_space_a_b",
8815. 3093 right,
8816. 3093 artifact(left),
8817. 3093 artifact(right)
8818. 3093 );
8819. 3093 }
8820. 3093 } else {
8821. 1
8822. 1// from:
8823. 1// } else if (
8824. 1// right.arity === "binary"
8825. 1// && right.id === "("
8826. 1// && free
8827. 1// ) {
8828. 1// no_space();
8829. 1// } else if (
8830. 1
8831. 1// Probably deadcode.
8832. 1// if (open) {
8833. 1// const at = (
8834. 1// free
8835. 1// ? margin
8836. 1// : margin + 8
8837. 1// );
8838. 1// if (right.from < at) {
8839. 1// expected_at(at);
8840. 1// }
8841. 1// } else {
8842. 1// if (right.from !== margin + 8) {
8843. 1// expected_at(margin + 8);
8844. 1// }
8845. 1// }
8846. 1
8847. 1 jslint_assert(open, `Expected open.`);
8848. 1 jslint_assert(free, `Expected free.`);
8849. 1 if (right.from < margin) {
8850. 1
8851. 1// test_cause:
8852. 1// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1]
8853. 1
8854. 1 expected_at(margin);
8855. 1 }
8856. 1 }
8857. 3094 }
8858. 208
8859. 95940 function no_space_only() {
8860. 95940 if (
8861. 95940 left.id !== "(global)"
8862. 95938 && left.nr + 1 === right.nr
8863. 95938 && (
8864. 95938 left.line !== right.line
8865. 95938 || left.thru !== right.from
8866. 95938 )
8867. 5 ) {
8868. 5 warn(
8869. 5 "unexpected_space_a_b",
8870. 5 right,
8871. 5 artifact(left),
8872. 5 artifact(right)
8873. 5 );
8874. 5 }
8875. 95940 }
8876. 208
8877. 46137 function one_space() {
8878. 44240 if (left.line === right.line || !open) {
8879. 44240 if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
8880. 44240 warn(
8881. 44240 "expected_space_a_b",
8882. 44240 right,
8883. 44240 artifact(left),
8884. 44240 artifact(right)
8885. 44240 );
8886. 44240 }
8887. 44240 } else {
8888. 1897 if (right.from !== margin) {
8889. 1897 expected_at(margin);
8890. 1897 }
8891. 1897 }
8892. 46137 }
8893. 208
8894. 9345 function one_space_only() {
8895. 8 if (left.line !== right.line || left.thru + 1 !== right.from) {
8896. 8 warn("expected_space_a_b", right, artifact(left), artifact(right));
8897. 8 }
8898. 9345 }
8899. 208
8900. 24634 function pop() {
8901. 24634 const previous = function_stack.pop();
8902. 24634 closer = previous.closer;
8903. 24634 free = previous.free;
8904. 24634 margin = previous.margin;
8905. 24634 open = previous.open;
8906. 24634 opening = previous.opening;
8907. 24634 }
8908. 208
8909. 24634 function push() {
8910. 24634 function_stack.push({
8911. 24634 closer,
8912. 24634 free,
8913. 24634 margin,
8914. 24634 open,
8915. 24634 opening
8916. 24634 });
8917. 24634 }
8918. 208
8919. 208// uninitialized_and_unused();
8920. 208// Delve into the functions looking for variables that were not initialized
8921. 208// or used. If the file imports or exports, then its global object is also
8922. 208// delved.
8923. 208
8924. 174 if (state.mode_module === true || option_dict.node) {
8925. 51 delve(token_global);
8926. 51 }
8927. 208 catch_list.forEach(delve);
8928. 208 function_list.forEach(delve);
8929. 208
8930. 2 if (option_dict.white) {
8931. 2 return;
8932. 206 }
8933. 206
8934. 206// whitage();
8935. 206// Go through the token list, looking at usage of whitespace.
8936. 206
8937. 206858 token_list.forEach(function whitage(the_token) {
8938. 206858 right = the_token;
8939. 195699 if (right.id === "(comment)" || right.id === "(end)") {
8940. 11367 nr_comments_skipped += 1;
8941. 195491 } else {
8942. 195491
8943. 195491// If left is an opener and right is not the closer, then push the previous
8944. 195491// state. If the token following the opener is on the next line, then this is
8945. 195491// an open form. If the tokens are on the same line, then it is a closed form.
8946. 195491// Open form is more readable, with each item (statement, argument, parameter,
8947. 195491// etc) starting on its own line. Closed form is more compact. Statement blocks
8948. 195491// are always in open form.
8949. 195491
8950. 195491// The open and close pairs.
8951. 195491
8952. 195491 switch (left.id) {
8953. 195491 case "${":
8954. 195491 case "(":
8955. 195491 case "[":
8956. 195491 case "{":
8957. 195491
8958. 195491// test_cause:
8959. 195491// ["let aa=[];", "whitage", "opener", "", 0]
8960. 195491// ["let aa=`${0}`;", "whitage", "opener", "", 0]
8961. 195491// ["let aa=aa();", "whitage", "opener", "", 0]
8962. 195491// ["let aa={};", "whitage", "opener", "", 0]
8963. 195491
8964. 195491 test_cause("opener");
8965. 195491
8966. 195491// Probably deadcode.
8967. 195491// case "${}":
8968. 195491
8969. 195491 jslint_assert(
8970. 195491 !(left.id + right.id === "${}"),
8971. 195491 "Expected !(left.id + right.id === \"${}\")."
8972. 195491 );
8973. 195491 switch (left.id + right.id) {
8974. 195491 case "()":
8975. 195491 case "[]":
8976. 195491 case "{}":
8977. 195491
8978. 195491// If left and right are opener and closer, then the placement of right depends
8979. 195491// on the openness. Illegal pairs (like '{]') have already been detected.
8980. 195491
8981. 195491// test_cause:
8982. 195491// ["let aa=[];", "whitage", "opener_closer", "", 0]
8983. 195491// ["let aa=aa();", "whitage", "opener_closer", "", 0]
8984. 195491// ["let aa={};", "whitage", "opener_closer", "", 0]
8985. 195491
8986. 195491 test_cause("opener_closer");
8987. 195491 if (left.line === right.line) {
8988. 195491
8989. 195491// test_cause:
8990. 195491// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
8991. 195491
8992. 195491 no_space();
8993. 195491 } else {
8994. 195491
8995. 195491// test_cause:
8996. 195491// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2]
8997. 195491
8998. 195491 at_margin(0);
8999. 195491 }
9000. 195491 break;
9001. 195491 default:
9002. 195491
9003. 195491// test_cause:
9004. 195491// ["let aa=(0);", "whitage", "opener_operand", "", 0]
9005. 195491// ["let aa=[0];", "whitage", "opener_operand", "", 0]
9006. 195491// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0]
9007. 195491// ["let aa=aa(0);", "whitage", "opener_operand", "", 0]
9008. 195491// ["let aa={aa:0};", "whitage", "opener_operand", "", 0]
9009. 195491
9010. 195491 test_cause("opener_operand");
9011. 195491 opening = left.open || (left.line !== right.line);
9012. 195491 push();
9013. 195491 switch (left.id) {
9014. 195491 case "${":
9015. 195491 closer = "}";
9016. 195491 break;
9017. 195491 case "(":
9018. 195491 closer = ")";
9019. 195491 break;
9020. 195491 case "[":
9021. 195491 closer = "]";
9022. 195491 break;
9023. 195491 case "{":
9024. 195491 closer = "}";
9025. 195491 break;
9026. 195491 }
9027. 195491 if (opening) {
9028. 195491
9029. 195491// test_cause:
9030. 195491// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0]
9031. 195491// ["let aa=(\n0\n);", "whitage", "opening", "", 0]
9032. 195491// ["let aa=[\n0\n];", "whitage", "opening", "", 0]
9033. 195491// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0]
9034. 195491// ["let aa={\naa:0\n};", "whitage", "opening", "", 0]
9035. 195491
9036. 195491 test_cause("opening");
9037. 195491 free = closer === ")" && left.free;
9038. 195491 open = true;
9039. 195491 margin += mode_indent;
9040. 195491 if (right.role === "label") {
9041. 195491 if (right.from !== 0) {
9042. 195491
9043. 195491// test_cause:
9044. 195491// ["
9045. 195491// function aa() {
9046. 195491// bb:
9047. 195491// while (aa) {
9048. 195491// if (aa) {
9049. 195491// break bb;
9050. 195491// }
9051. 195491// }
9052. 195491// }
9053. 195491// ", "expected_at", "expected_a_at_b_c", "1", 2]
9054. 195491
9055. 195491 expected_at(0);
9056. 195491 }
9057. 195491 } else if (right.switch) {
9058. 195491 at_margin(-mode_indent);
9059. 195491 } else {
9060. 195491 at_margin(0);
9061. 195491 }
9062. 195491 } else {
9063. 195491 if (right.statement || right.role === "label") {
9064. 195491
9065. 195491// test_cause:
9066. 195491// ["
9067. 195491// function aa() {bb:
9068. 195491// while (aa) {
9069. 195491// aa();
9070. 195491// }
9071. 195491// }
9072. 195491// ", "whitage", "expected_line_break_a_b", "bb", 16]
9073. 195491
9074. 195491 warn(
9075. 195491 "expected_line_break_a_b",
9076. 195491 right,
9077. 195491 artifact(left),
9078. 195491 artifact(right)
9079. 195491 );
9080. 195491 }
9081. 195491
9082. 195491// test_cause:
9083. 195491// ["let aa=(0);", "whitage", "not_free", "", 0]
9084. 195491// ["let aa=[0];", "whitage", "not_free", "", 0]
9085. 195491// ["let aa=`${0}`;", "whitage", "not_free", "", 0]
9086. 195491// ["let aa={aa:0};", "whitage", "not_free", "", 0]
9087. 195491
9088. 195491 test_cause("not_free");
9089. 195491 free = false;
9090. 195491 open = false;
9091. 195491
9092. 195491// test_cause:
9093. 195491// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12]
9094. 195491
9095. 195491 no_space_only();
9096. 195491 }
9097. 195491 }
9098. 195491 break;
9099. 195491 default:
9100. 195491 if (right.statement === true) {
9101. 195491 if (left.id === "else") {
9102. 195491
9103. 195491// test_cause:
9104. 195491// ["
9105. 195491// let aa = 0;
9106. 195491// if (aa) {
9107. 195491// aa();
9108. 195491// } else if (aa) {
9109. 195491// aa();
9110. 195491// }
9111. 195491// ", "one_space_only", "expected_space_a_b", "if", 9]
9112. 195491
9113. 195491 one_space_only();
9114. 195491 } else {
9115. 195491
9116. 195491// test_cause:
9117. 195491// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2]
9118. 195491
9119. 195491 at_margin(0);
9120. 195491 open = false;
9121. 195491 }
9122. 195491
9123. 195491// If right is a closer, then pop the previous state.
9124. 195491
9125. 195491 } else if (right.id === closer) {
9126. 195491 pop();
9127. 195491 if (opening && right.id !== ";") {
9128. 195491 at_margin(0);
9129. 195491 } else {
9130. 195491 no_space_only();
9131. 195491 }
9132. 195491 } else {
9133. 195491
9134. 195491// Left is not an opener, and right is not a closer.
9135. 195491// The nature of left and right will determine the space between them.
9136. 195491
9137. 195491// If left is ',' or ';' or right is a statement then if open,
9138. 195491// right must go at the margin, or if closed, a space between.
9139. 195491
9140. 195491 if (right.switch) {
9141. 195491 at_margin(-mode_indent);
9142. 195491 } else if (right.role === "label") {
9143. 195491 if (right.from !== 0) {
9144. 195491
9145. 195491// test_cause:
9146. 195491// ["
9147. 195491// function aa() {
9148. 195491// aa();cc:
9149. 195491// while (aa) {
9150. 195491// if (aa) {
9151. 195491// break cc;
9152. 195491// }
9153. 195491// }
9154. 195491// }
9155. 195491// ", "expected_at", "expected_a_at_b_c", "1", 10]
9156. 195491
9157. 195491 expected_at(0);
9158. 195491 }
9159. 195491 } else if (left.id === ",") {
9160. 195491 if (!open || (
9161. 195491 (free || closer === "]")
9162. 195491 && left.line === right.line
9163. 195491 )) {
9164. 195491
9165. 195491// test_cause:
9166. 195491// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9]
9167. 195491
9168. 195491 one_space();
9169. 195491 } else {
9170. 195491
9171. 195491// test_cause:
9172. 195491// ["
9173. 195491// function aa() {
9174. 195491// aa(
9175. 195491// 0,0
9176. 195491// );
9177. 195491// }
9178. 195491// ", "expected_at", "expected_a_at_b_c", "9", 11]
9179. 195491
9180. 195491 at_margin(0);
9181. 195491 }
9182. 195491
9183. 195491// If right is a ternary operator, line it up on the margin.
9184. 195491
9185. 195491 } else if (right.arity === "ternary") {
9186. 195491 if (open) {
9187. 195491
9188. 195491// test_cause:
9189. 195491// ["
9190. 195491// let aa = (
9191. 195491// aa
9192. 195491// ? 0
9193. 195491// : 1
9194. 195491// );
9195. 195491// ", "expected_at", "expected_a_at_b_c", "5", 1]
9196. 195491
9197. 195491 at_margin(0);
9198. 195491 } else {
9199. 195491
9200. 195491// test_cause:
9201. 195491// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14]
9202. 195491
9203. 195491 warn("use_open", right);
9204. 195491 }
9205. 195491 } else if (
9206. 195491 right.arity === "binary"
9207. 195491 && right.id === "("
9208. 195491 && free
9209. 195491 ) {
9210. 195491
9211. 195491// test_cause:
9212. 195491// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4]
9213. 195491
9214. 195491 no_space();
9215. 195491 } else if (
9216. 195491 left.id === "."
9217. 195491 || left.id === "?."
9218. 195491 || left.id === "..."
9219. 195491 || right.id === ","
9220. 195491 || right.id === ";"
9221. 195491 || right.id === ":"
9222. 195491 || (
9223. 195491 right.arity === "binary"
9224. 195491 && (right.id === "(" || right.id === "[")
9225. 195491 )
9226. 195491 || (
9227. 195491 right.arity === "function"
9228. 195491 && left.id !== "function"
9229. 195491 )
9230. 195491 || (right.id === "." || right.id === "?.")
9231. 195491 ) {
9232. 195491
9233. 195491// test_cause:
9234. 195491// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12]
9235. 195491// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13]
9236. 195491
9237. 195491 no_space_only();
9238. 195491 } else if (left.id === ";") {
9239. 195491
9240. 195491// test_cause:
9241. 195491// ["
9242. 195491// /*jslint for*/
9243. 195491// function aa() {
9244. 195491// for (
9245. 195491// aa();
9246. 195491// aa;
9247. 195491// aa()
9248. 195491// ) {
9249. 195491// aa();
9250. 195491// }
9251. 195491// }
9252. 195491// ", "expected_at", "expected_a_at_b_c", "9", 1]
9253. 195491
9254. 195491 if (open) {
9255. 195491 at_margin(0);
9256. 195491 }
9257. 195491 } else if (
9258. 195491 left.arity === "ternary"
9259. 195491 || left.id === "case"
9260. 195491 || left.id === "catch"
9261. 195491 || left.id === "else"
9262. 195491 || left.id === "finally"
9263. 195491 || left.id === "while"
9264. 195491 || left.id === "await"
9265. 195491 || right.id === "catch"
9266. 195491 || right.id === "else"
9267. 195491 || right.id === "finally"
9268. 195491 || (right.id === "while" && !right.statement)
9269. 195491 || (left.id === ")" && right.id === "{")
9270. 195491 ) {
9271. 195491
9272. 195491// test_cause:
9273. 195491// ["
9274. 195491// function aa() {
9275. 195491// do {
9276. 195491// aa();
9277. 195491// } while(aa());
9278. 195491// }
9279. 195491// ", "one_space_only", "expected_space_a_b", "(", 12]
9280. 195491
9281. 195491 one_space_only();
9282. 195491 } else if (
9283. 195491
9284. 195491// There is a space between left and right.
9285. 195491
9286. 195491 spaceop[left.id] === true
9287. 195491 || spaceop[right.id] === true
9288. 195491 || (
9289. 195491 left.arity === "binary"
9290. 195491 && (left.id === "+" || left.id === "-")
9291. 195491 )
9292. 195491 || (
9293. 195491 right.arity === "binary"
9294. 195491 && (right.id === "+" || right.id === "-")
9295. 195491 )
9296. 195491 || left.id === "function"
9297. 195491 || left.id === ":"
9298. 195491 || left.id === "async"
9299. 195491 || (
9300. 195491 (
9301. 195491 left.identifier
9302. 195491 || left.id === "(string)"
9303. 195491 || left.id === "(number)"
9304. 195491 )
9305. 195491 && (
9306. 195491 right.identifier
9307. 195491 || right.id === "(string)"
9308. 195491 || right.id === "(number)"
9309. 195491 )
9310. 195491 )
9311. 195491 || (left.arity === "statement" && right.id !== ";")
9312. 195491 ) {
9313. 195491
9314. 195491// test_cause:
9315. 195491// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8]
9316. 195491// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1]
9317. 195491
9318. 195491 one_space();
9319. 195491 } else if (left.arity === "unary" && left.id !== "`") {
9320. 195491 no_space_only();
9321. 195491 }
9322. 195491 }
9323. 195491 }
9324. 195491 nr_comments_skipped = 0;
9325. 195491 delete left.calls;
9326. 195491 delete left.dead;
9327. 195491 delete left.free;
9328. 195491 delete left.init;
9329. 195491 delete left.open;
9330. 195491 delete left.used;
9331. 195491 left = right;
9332. 195491 }
9333. 206858 });
9334. 206}
9335. 1
9336. 6function jslint_report({
9337. 6 exports,
9338. 6 froms,
9339. 6 functions,
9340. 6 global,
9341. 6 json,
9342. 6 module,
9343. 6 property,
9344. 6 stop,
9345. 6 warnings
9346. 6}) {
9347. 6
9348. 6// This function will create human-readable, html-report
9349. 6// for warnings, properties, and functions from jslint-result-object.
9350. 6//
9351. 6// Example usage:
9352. 6// let result = jslint("console.log('hello world')");
9353. 6// let html = jslint_report(result);
9354. 6
9355. 6 let html = "";
9356. 6 let length_80 = 1111;
9357. 6
9358. 328 function address(line = 1, column = 1) {
9359. 328
9360. 328// This function will create HTML address element from <line> and <column>
9361. 328
9362. 328 return `<address>${Number(line)}: ${Number(column)}</address>`;
9363. 328
9364. 328 }
9365. 6
9366. 2256 function detail(title, list) {
9367. 2256 return (
9368. 2256 (Array.isArray(list) && list.length > 0)
9369. 780 ? (
9370. 780
9371. 780// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
9372. 780// <dt> and <dd> groups, <script>, <template> or <div> elements.
9373. 780
9374. 780 "<dl>"
9375. 780 + "<dt>" + htmlEscape(title) + "</dt>"
9376. 780 + "<dd>" + list.join(", ") + "</dd>"
9377. 780 + "</dl>"
9378. 780 )
9379. 1476 : ""
9380. 2256 );
9381. 2256 }
9382. 6
9383. 6 html += String(`
9384. 6<style class="JSLINT_REPORT_STYLE">
9385. 6/* jslint utility2:true */
9386. 6/*csslint box-model: false, ids:false */
9387. 6/*csslint ignore:start*/
9388. 6@font-face {
9389. 6 font-display: swap;
9390. 6 font-family: "Daley";
9391. 6 src: url(
9392. 6"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
9393. 6AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
9394. 67NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
9395. 6KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
9396. 6llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
9397. 67sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
9398. 6X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
9399. 6fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
9400. 6/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
9401. 6gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
9402. 6l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
9403. 6r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
9404. 6J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
9405. 6weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
9406. 6/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
9407. 6EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
9408. 6DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
9409. 6qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
9410. 6r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
9411. 6Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
9412. 6jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
9413. 6cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
9414. 6c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
9415. 6Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
9416. 6fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
9417. 6OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
9418. 6Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
9419. 6mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
9420. 6UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
9421. 66cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
9422. 60EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
9423. 69KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
9424. 6ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
9425. 6bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
9426. 6ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
9427. 6lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
9428. 6mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
9429. 67HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
9430. 6prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
9431. 6OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
9432. 6pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
9433. 6H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
9434. 6knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
9435. 6aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
9436. 6ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
9437. 6JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
9438. 66iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
9439. 6FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
9440. 6nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
9441. 6edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
9442. 6T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
9443. 6OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
9444. 6nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
9445. 6c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
9446. 6ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
9447. 61JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
9448. 65o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
9449. 6b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
9450. 6/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
9451. 6DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
9452. 6tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
9453. 6C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
9454. 6wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
9455. 6vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
9456. 6vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
9457. 6tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
9458. 6/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
9459. 6wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
9460. 6qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
9461. 629CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
9462. 6eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
9463. 6s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
9464. 6xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
9465. 6c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
9466. 60WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
9467. 6+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
9468. 6mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
9469. 6U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
9470. 6kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
9471. 6Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
9472. 6AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
9473. 6O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
9474. 6R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
9475. 6wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
9476. 63gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
9477. 6r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
9478. 6+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
9479. 6icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
9480. 67frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
9481. 6j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
9482. 6wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
9483. 6I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
9484. 6ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
9485. 63FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
9486. 6OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
9487. 6Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
9488. 6xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
9489. 6dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
9490. 6aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
9491. 6sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
9492. 63v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
9493. 69GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
9494. 659iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
9495. 6O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
9496. 6Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
9497. 6aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
9498. 6nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
9499. 6CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
9500. 67ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
9501. 6PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
9502. 6X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
9503. 6dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
9504. 6ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
9505. 6gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
9506. 6ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
9507. 6nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
9508. 6+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
9509. 6lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
9510. 6OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
9511. 6v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
9512. 69uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
9513. 6b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
9514. 6RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
9515. 659yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
9516. 68U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
9517. 656J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
9518. 6K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
9519. 6+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
9520. 6La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
9521. 6K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
9522. 65Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
9523. 6WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
9524. 6+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
9525. 6SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
9526. 6pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
9527. 61R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
9528. 6/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
9529. 6 ) format("woff2");
9530. 6}
9531. 6.JSLINT_,
9532. 6.JSLINT_ address,
9533. 6.JSLINT_ button,
9534. 6.JSLINT_ cite,
9535. 6.JSLINT_ dd,
9536. 6.JSLINT_ dfn,
9537. 6.JSLINT_ dl,
9538. 6.JSLINT_ dt,
9539. 6.JSLINT_ fieldset,
9540. 6.JSLINT_ fieldset > div,
9541. 6.JSLINT_ input,
9542. 6.JSLINT_ label,
9543. 6.JSLINT_ legend,
9544. 6.JSLINT_ ol,
9545. 6.JSLINT_ samp,
9546. 6.JSLINT_ style,
9547. 6.JSLINT_ textarea,
9548. 6.JSLINT_ ul {
9549. 6 border: 0;
9550. 6 box-sizing: border-box;
9551. 6 margin: 0;
9552. 6 padding: 0;
9553. 6}
9554. 6/* disable text inflation algorithm used on some smartphones and tablets */
9555. 6.JSLINT_ {
9556. 6 -ms-text-size-adjust: none;
9557. 6 -webkit-text-size-adjust: none;
9558. 6 text-size-adjust: none;
9559. 6}
9560. 6.JSLINT_REPORT_ div {
9561. 6 box-sizing: border-box;
9562. 6}
9563. 6/*csslint ignore:end*/
9564. 6
9565. 6/* css - jslint_report - font */
9566. 6.JSLINT_,
9567. 6.JSLINT_ fieldset legend,
9568. 6.JSLINT_ .center {
9569. 6 font-family: daley, sans-serif;
9570. 6 font-size: 14px;
9571. 6}
9572. 6.JSLINT_ fieldset textarea,
9573. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
9574. 6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
9575. 6 font-size: 12px;
9576. 6}
9577. 6.JSLINT_ fieldset textarea,
9578. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
9579. 6 font-family: monospace;
9580. 6}
9581. 6.JSLINT_ fieldset > div {
9582. 6 font-family: sans-serif;
9583. 6}
9584. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
9585. 6 font-style: normal;
9586. 6 font-weight: bold;
9587. 6}
9588. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
9589. 6 font-style: italic;
9590. 6}
9591. 6.JSLINT_ #JSLINT_REPORT_TITLE {
9592. 6 font-size: 32px;
9593. 6}
9594. 6
9595. 6/* css - jslint_report - general */
9596. 6.JSLINT_ {
9597. 6 background: antiquewhite;
9598. 6}
9599. 6.JSLINT_ fieldset {
9600. 6 background: gainsboro;
9601. 6 clear: both;
9602. 6 margin: 16px 40px;
9603. 6 width: auto;
9604. 6}
9605. 6.JSLINT_ fieldset address {
9606. 6 float: right;
9607. 6}
9608. 6.JSLINT_ fieldset legend,
9609. 6.JSLINT_ .center {
9610. 6 text-align: center;
9611. 6}
9612. 6.JSLINT_ fieldset legend {
9613. 6 background: darkslategray;
9614. 6 color: white;
9615. 6 padding: 4px 0;
9616. 6 width: 100%;
9617. 6}
9618. 6.JSLINT_ fieldset textarea {
9619. 6 padding: 4px;
9620. 6 resize: none;
9621. 6 white-space: pre;
9622. 6 width: 100%;
9623. 6}
9624. 6.JSLINT_ fieldset textarea::selection {
9625. 6 background: wheat;
9626. 6}
9627. 6.JSLINT_ fieldset > div {
9628. 6 padding: 16px;
9629. 6 width: 100%;
9630. 6 word-wrap: break-word;
9631. 6}
9632. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
9633. 6 background: cornsilk;
9634. 6 padding: 8px 16px;
9635. 6}
9636. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
9637. 6 line-height: 20px;
9638. 6 padding-left: 120px;
9639. 6}
9640. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
9641. 6 display: block;
9642. 6 line-height: 20px;
9643. 6}
9644. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
9645. 6 position: relative
9646. 6}
9647. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
9648. 6 line-height: 20px;
9649. 6 position: absolute;
9650. 6 text-align: right;
9651. 6 width: 100px;
9652. 6}
9653. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
9654. 6 background: white;
9655. 6}
9656. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
9657. 6 /* yellow */
9658. 6 background: #ffffe0;
9659. 6 margin-left: 16px;
9660. 6}
9661. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
9662. 6 /* green */
9663. 6 background: #e0ffe0;
9664. 6 margin-left: 32px;
9665. 6}
9666. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
9667. 6 /* blue */
9668. 6 background: #D0D0ff;
9669. 6 margin-left: 48px;
9670. 6}
9671. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
9672. 6 /* purple */
9673. 6 background: #ffe0ff;
9674. 6 margin-left: 64px;
9675. 6}
9676. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
9677. 6 /* red */
9678. 6 background: #ffe0e0;
9679. 6 margin-left: 80px;
9680. 6}
9681. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
9682. 6 /* orange */
9683. 6 background: #ffe390;
9684. 6 margin-left: 96px;
9685. 6}
9686. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
9687. 6 /* gray */
9688. 6 background: #e0e0e0;
9689. 6 margin-left: 112px;
9690. 6}
9691. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
9692. 6 margin-left: 128px;
9693. 6}
9694. 6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
9695. 6 margin-left: 144px;
9696. 6}
9697. 6.JSLINT_ #JSLINT_REPORT_PROPERTIES {
9698. 6 background: transparent;
9699. 6}
9700. 6.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
9701. 6 background: honeydew;
9702. 6 height: 100px;
9703. 6}
9704. 6.JSLINT_ #JSLINT_REPORT_TITLE {
9705. 6 color: darkslategray;
9706. 6 padding-top: 16px;
9707. 6}
9708. 6.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
9709. 6 display: block;
9710. 6 margin: 16px 0 4px 0;
9711. 6 overflow-x: hidden;
9712. 6 white-space: pre-line;
9713. 6}
9714. 6.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
9715. 6 margin-top: 0;
9716. 6}
9717. 6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
9718. 6 background: lavenderblush;
9719. 6 display: block;
9720. 6 padding: 4px;
9721. 6 white-space: pre-wrap;
9722. 6}
9723. 6.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
9724. 6 background: pink;
9725. 6 max-height: 400px;
9726. 6 overflow-y: auto;
9727. 6}
9728. 6.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
9729. 6/* Google Lighthouse Accessibility - Background and foreground colors do not */
9730. 6/* have a sufficient contrast ratio. */
9731. 6 /* background: indianred; */
9732. 6 background: #b44;
9733. 6}
9734. 6</style>
9735. 6 `).trim() + "\n";
9736. 6
9737. 6// Produce the Title.
9738. 6
9739. 6 html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
9740. 6 html += "JSLint Report\n";
9741. 6 html += "</div>\n";
9742. 6
9743. 6// Produce the HTML Error Report.
9744. 6// <cite>
9745. 6// <address>LINE_NUMBER</address>
9746. 6// MESSAGE
9747. 6// </cite>
9748. 6// <samp>EVIDENCE</samp>
9749. 6
9750. 6 html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
9751. 6 html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
9752. 6 html += "<div>\n";
9753. 1 if (stop) {
9754. 1 html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
9755. 1 }
9756. 7 warnings.forEach(function ({
9757. 7 column,
9758. 7 line,
9759. 7 line_source,
9760. 7 message,
9761. 7 stack_trace = ""
9762. 7 }, ii) {
9763. 7 html += (
9764. 7 "<cite>"
9765. 7 + address(line, column)
9766. 7 + htmlEscape((ii + 1) + ". " + message)
9767. 7 + "</cite>"
9768. 7 + "<samp>"
9769. 7 + htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace)
9770. 7 + "</samp>\n"
9771. 7 );
9772. 7 });
9773. 3 if (warnings.length === 0) {
9774. 3 html += "<div class=\"center\">There are no warnings.</div>\n";
9775. 3 }
9776. 6 html += "</div>\n";
9777. 6 html += "</fieldset>\n";
9778. 6
9779. 6// Produce the /*property*/ directive.
9780. 6
9781. 6 html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
9782. 6 html += (
9783. 6 "<legend>Report: Properties ("
9784. 6 + Object.keys(property).length
9785. 6 + ")</legend>\n"
9786. 6 );
9787. 6 html += "<label>\n";
9788. 6 html += "<textarea readonly>";
9789. 6 html += "/*property";
9790. 300 Object.keys(property).sort().forEach(function (key, ii) {
9791. 299 if (ii !== 0) {
9792. 299 html += ",";
9793. 299 length_80 += 2;
9794. 299 }
9795. 42 if (length_80 + key.length >= 80) {
9796. 42 length_80 = 4;
9797. 42 html += "\n ";
9798. 42 }
9799. 300 html += " " + key;
9800. 300 length_80 += key.length;
9801. 300 });
9802. 6 html += "\n*/\n";
9803. 6 html += "</textarea>\n";
9804. 6 html += "</label>\n";
9805. 6 html += "</fieldset>\n";
9806. 6
9807. 6// Produce the HTML Function Report.
9808. 6// <div class=LEVEL>
9809. 6// <address>LINE_NUMBER</address>
9810. 6// <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
9811. 6// <dl>
9812. 6// <dt>DETAIL</dt>
9813. 6// <dd>NAMES</dd>
9814. 6// </dl>
9815. 6// </div>
9816. 6
9817. 6 html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
9818. 6 html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
9819. 6 html += "<div>\n";
9820. 2 if (json) {
9821. 2
9822. 2// Bugfix - fix website crashing when linting pure json-object.
9823. 2// return (
9824. 2
9825. 2 html += (
9826. 2 warnings.length === 0
9827. 2 ? "<div class=\"center\">JSON: good.</div>\n"
9828. 2 : "<div class=\"center\">JSON: bad.</div>\n"
9829. 2 );
9830. 4 } else if (functions.length === 0) {
9831. 4 html += "<div class=\"center\">There are no functions.</div>\n";
9832. 4 }
9833. 6 exports = Object.keys(exports).sort();
9834. 6 froms.sort();
9835. 6 global = Object.keys(global.context).sort();
9836. 6 module = (
9837. 6 module
9838. 1 ? "module"
9839. 5 : "global"
9840. 6 );
9841. 3 if (global.length + froms.length + exports.length > 0) {
9842. 3 if (functions.length === 0) {
9843. 3 html += "<br>\n";
9844. 3 }
9845. 3 html += "<div class=\"level level0\">\n";
9846. 3 html += detail(module, global);
9847. 3 html += detail("import from", froms);
9848. 3 html += detail("export", exports);
9849. 3 html += "</div>\n";
9850. 3 }
9851. 321 functions.forEach(function (the_function) {
9852. 321 let {
9853. 321 context,
9854. 321 from,
9855. 321 id,
9856. 321 level,
9857. 321 line,
9858. 321 name,
9859. 321
9860. 321// Bugfix - fix html-report from crashing if parameters is undefined.
9861. 321
9862. 321 parameters = [],
9863. 321 signature
9864. 321 } = the_function;
9865. 321 let list = Object.keys(context);
9866. 321 let params;
9867. 321 html += (
9868. 321 "<div class=\"level level" + htmlEscape(level) + "\">"
9869. 321 + address(line, from + 1)
9870. 321 + "<dfn>"
9871. 321 + (
9872. 321 id === "=>"
9873. 1 ? (
9874. 1 "\u00ab" + htmlEscape(name) + "\u00bb"
9875. 1 + htmlEscape(signature)
9876. 1 + " =>"
9877. 1 )
9878. 320 : (
9879. 320 typeof name === "string"
9880. 320 ? "\u00ab" + htmlEscape(name) + "\u00bb"
9881. 320 : htmlEscape(name.id)
9882. 320 ) + htmlEscape(signature)
9883. 321 )
9884. 321 + "</dfn>"
9885. 321 );
9886. 321 params = [];
9887. 470 parameters.forEach(function extract({
9888. 470 id,
9889. 470 names
9890. 470 }) {
9891. 470 switch (id) {
9892. 6 case "[":
9893. 42 case "{":
9894. 42
9895. 42// Recurse extract().
9896. 42
9897. 42 names.forEach(extract);
9898. 42 break;
9899. 4 case "ignore":
9900. 4 break;
9901. 424 default:
9902. 424 params.push(id);
9903. 470 }
9904. 470 });
9905. 321 html += detail("parameter", params.sort());
9906. 321 list.sort();
9907. 2202 html += detail("variable", list.filter(function (id) {
9908. 2202 return (
9909. 2202 context[id].role === "variable"
9910. 1692 && context[id].parent === the_function
9911. 2202 );
9912. 2202 }));
9913. 2202 html += detail("exception", list.filter(function (id) {
9914. 2202 return context[id].role === "exception";
9915. 2202 }));
9916. 2202 html += detail("closure", list.filter(function (id) {
9917. 2202 return (
9918. 2202 context[id].closure === true
9919. 1494 && context[id].parent === the_function
9920. 2202 );
9921. 2202 }));
9922. 2202 html += detail("outer", list.filter(function (id) {
9923. 2202 return (
9924. 2202 context[id].parent !== the_function
9925. 1190 && context[id].parent.id !== "(global)"
9926. 2202 );
9927. 2202 }));
9928. 2202 html += detail(module, list.filter(function (id) {
9929. 2202 return context[id].parent.id === "(global)";
9930. 2202 }));
9931. 2202 html += detail("label", list.filter(function (id) {
9932. 2202 return context[id].role === "label";
9933. 2202 }));
9934. 321 html += "</div>\n";
9935. 321 });
9936. 6 html += "</div>\n";
9937. 6 html += "</fieldset>\n";
9938. 6 return html;
9939. 6}
9940. 1
9941. 10async function jstestDescribe(description, testFunction) {
9942. 10
9943. 10// This function will create-and-run test-group <testFunction>
9944. 10// with given <description>.
9945. 10
9946. 10 let message;
9947. 10 let result;
9948. 10 let timerTimeout;
9949. 10
9950. 10// Init jstestTimeStart.
9951. 10
9952. 1 if (jstestTimeStart === undefined) {
9953. 1 jstestTimeStart = jstestTimeStart || Date.now();
9954. 1 process.on("exit", jstestOnExit);
9955. 1 }
9956. 10
9957. 10// PR-457 - Wait awhile for imports to initialize.
9958. 10
9959. 10 await new Promise(function (resolve) {
9960. 10 setTimeout(resolve);
9961. 10 });
9962. 10
9963. 10// Init jstestItList.
9964. 10
9965. 10 jstestItList = [];
9966. 10 testFunction();
9967. 10
9968. 10// Wait for jstestItList to resolve.
9969. 10
9970. 10 timerTimeout = setTimeout(noop, 0x7fffffff);
9971. 10 result = await Promise.all(jstestItList);
9972. 10 clearTimeout(timerTimeout);
9973. 10
9974. 10// Print test results.
9975. 10
9976. 10 message = (
9977. 10 "\n " + (Date.now() - jstestTimeStart) + "ms"
9978. 10 + " - test describe - " + description + "\n"
9979. 66 + result.map(function ([
9980. 66 err, description, mode
9981. 66 ]) {
9982. 66 jstestItCount += 1;
9983. 1 if (err) {
9984. 1 jstestCountFailed += 1;
9985. 1 err = (
9986. 1 " \u001b[31m\u2718 " + jstestItCount + ". test it - "
9987. 1 + description + "\n" + err.stack + "\u001b[39m"
9988. 1 );
9989. 1 if (mode === "pass") {
9990. 1 jstestCountFailed -= 1;
9991. 1 err = "";
9992. 1 }
9993. 1 }
9994. 66 return err || (
9995. 66 " \u001b[32m\u2714 " + jstestItCount + ". test it - "
9996. 66 + description + "\u001b[39m"
9997. 66 );
9998. 66 }).join("\n")
9999. 10 );
10000. 10 console.error(message);
10001. 10}
10002. 1
10003. 66function jstestIt(description, testFunction, mode) {
10004. 66
10005. 66// This function will create-and-run test-case <testFunction>
10006. 66// inside current test-group with given <description>.
10007. 66
10008. 66 jstestCountTotal += 1;
10009. 66 jstestItList.push(new Promise(async function (resolve) {
10010. 66 let err;
10011. 66 try {
10012. 65 await testFunction();
10013. 65 } catch (errCaught) {
10014. 1 err = errCaught;
10015. 1 }
10016. 66 resolve([err, description, mode]);
10017. 66 }));
10018. 66}
10019. 1
10020. 2function jstestOnExit(exitCode, mode) {
10021. 2
10022. 2// This function will on process-exit, print test-report
10023. 2// and exit with non-zero exit-code if any test failed.
10024. 2
10025. 2 let message = (
10026. 2 (
10027. 2 (jstestCountFailed || mode === "testsFailed")
10028. 1 ? "\n\u001b[31m"
10029. 1 : "\n\u001b[32m"
10030. 2 )
10031. 2 + " tests total - " + jstestCountTotal + "\n"
10032. 2 + " tests failed - " + jstestCountFailed + "\n"
10033. 2 + "\n"
10034. 2 + " time finished - "
10035. 2 + Number(Date.now() - jstestTimeStart).toLocaleString()
10036. 2 + " ms\n"
10037. 2 + "\u001b[39m"
10038. 2 );
10039. 1 if (mode !== "testsFailed") {
10040. 1 console.error(message);
10041. 1 }
10042. 2 process.exitCode = exitCode || jstestCountFailed;
10043. 2 return message;
10044. 2}
10045. 1
10046. 107async function moduleFsInit() {
10047. 107
10048. 107// This function will import nodejs builtin-modules if they have not yet been
10049. 107// imported.
10050. 107
10051. 107// State 3 - Modules already imported.
10052. 107
10053. 104 if (moduleFs !== undefined) {
10054. 104 return;
10055. 104 }
10056. 3
10057. 3// State 2 - Wait while modules are importing.
10058. 3
10059. 3 if (moduleFsInitResolveList !== undefined) {
10060. 2 return new Promise(function (resolve) {
10061. 2 moduleFsInitResolveList.push(resolve);
10062. 2 });
10063. 2 }
10064. 1
10065. 1// State 1 - Start importing modules.
10066. 1
10067. 1 moduleFsInitResolveList = [];
10068. 1 [
10069. 1 moduleChildProcess,
10070. 1 moduleFs,
10071. 1 modulePath,
10072. 1 moduleUrl
10073. 1 ] = await Promise.all([
10074. 1 import("child_process"),
10075. 1 import("fs"),
10076. 1 import("path"),
10077. 1 import("url")
10078. 1 ]);
10079. 2 while (moduleFsInitResolveList.length > 0) {
10080. 2 moduleFsInitResolveList.shift()();
10081. 2 }
10082. 107}
10083. 1
10084. 2728function noop(val) {
10085. 2728
10086. 2728// This function will do nothing except return <val>.
10087. 2728
10088. 2728 return val;
10089. 2728}
10090. 1
10091. 12919function objectDeepCopyWithKeysSorted(obj) {
10092. 12919
10093. 12919// This function will recursively deep-copy <obj> with keys sorted.
10094. 12919
10095. 12919 let sorted;
10096. 8995 if (typeof obj !== "object" || !obj) {
10097. 8995 return obj;
10098. 8995 }
10099. 3924
10100. 3924// Recursively deep-copy list with child-keys sorted.
10101. 3924
10102. 3924 if (Array.isArray(obj)) {
10103. 1338 return obj.map(objectDeepCopyWithKeysSorted);
10104. 2586 }
10105. 2586
10106. 2586// Recursively deep-copy obj with keys sorted.
10107. 2586
10108. 2586 sorted = Object.create(null);
10109. 7457 Object.keys(obj).sort().forEach(function (key) {
10110. 7457 sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
10111. 7457 });
10112. 2586 return sorted;
10113. 2586}
10114. 1
10115. 2783function object_assign_from_list(dict, list, val) {
10116. 2783
10117. 2783// Assign each property-name from <list> to <dict>.
10118. 2783
10119. 89614 list.forEach(function (key) {
10120. 89614 dict[key] = val;
10121. 89614 });
10122. 2783 return dict;
10123. 2783}
10124. 1
10125. 97function v8CoverageListMerge(processCovs) {
10126. 97
10127. 97// This function is derived from MIT Licensed v8-coverage at
10128. 97// https://github.com/demurgos/v8-coverage/tree/master/ts
10129. 97// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
10130. 97//
10131. 97// Merges a list of v8 process coverages.
10132. 97// The result is normalized.
10133. 97// The input values may be mutated, it is not safe to use them after passing
10134. 97// them to this function.
10135. 97// The computation is synchronous.
10136. 97// @param processCovs Process coverages to merge.
10137. 97// @return Merged process coverage.
10138. 97
10139. 97 let resultMerged = []; // List of merged scripts from processCovs.
10140. 97 let urlToScriptDict = new Map(); // Map scriptCov.url to scriptCovs.
10141. 97
10142. 1094 function compareRangeList(aa, bb) {
10143. 1094
10144. 1094// Compares two range coverages.
10145. 1094// The ranges are first ordered by ascending `startOffset` and then by
10146. 1094// descending `endOffset`.
10147. 1094// This corresponds to a pre-order tree traversal.
10148. 1094
10149. 1065 if (aa.startOffset !== bb.startOffset) {
10150. 1065 return aa.startOffset - bb.startOffset;
10151. 1065 }
10152. 29 return bb.endOffset - aa.endOffset;
10153. 29 }
10154. 97
10155. 1707 function dictKeyValueAppend(dict, key, val) {
10156. 1707
10157. 1707// This function will append <val> to list <dict>[<key>].
10158. 1707
10159. 1707 let list = dict.get(key);
10160. 1165 if (list === undefined) {
10161. 1165 list = [];
10162. 1165 dict.set(key, list);
10163. 1165 }
10164. 1707 list.push(val);
10165. 1707 }
10166. 97
10167. 384 function mergeTreeList(parentTrees) {
10168. 384
10169. 384// This function will return RangeTree object with <parentTrees> merged into
10170. 384// property-children.
10171. 384// @precondition Same `start` and `end` for all the parentTrees
10172. 384
10173. 119 if (parentTrees.length <= 1) {
10174. 119 return parentTrees[0];
10175. 265 }
10176. 265
10177. 265// new RangeTree().
10178. 265
10179. 265 return {
10180. 265
10181. 265// Merge parentTrees into property-children.
10182. 265
10183. 265 children: mergeTreeListToChildren(parentTrees),
10184. 669 delta: parentTrees.reduce(function (aa, bb) {
10185. 669 return aa + bb.delta;
10186. 669 }, 0),
10187. 265 end: parentTrees[0].end,
10188. 265 start: parentTrees[0].start
10189. 265 };
10190. 265 }
10191. 97
10192. 265 function mergeTreeListToChildren(parentTrees) {
10193. 265
10194. 265// This function will return <resultChildren> with <parentTrees> merged.
10195. 265
10196. 265 let openRange;
10197. 265 let parentToChildDict = new Map(); // Map parent to child.
10198. 265 let queueList;
10199. 265 let queueListIi = 0;
10200. 265 let queueOffset;
10201. 265 let queueTrees;
10202. 265 let resultChildren = [];
10203. 265 let startToTreeDict = new Map(); // Map tree.start to tree.
10204. 639 function nextXxx() {
10205. 639
10206. 639// Increment nextOffset, nextTrees.
10207. 639
10208. 639 let [
10209. 639 nextOffset, nextTrees
10210. 300 ] = queueList[queueListIi] || [];
10211. 639 let openRangeEnd;
10212. 583 if (queueTrees === undefined) {
10213. 583 queueListIi += 1;
10214. 583
10215. 583// Increment nextOffset, nextTrees.
10216. 583
10217. 583 } else if (nextOffset === undefined || nextOffset > queueOffset) {
10218. 56 nextOffset = queueOffset;
10219. 56 nextTrees = queueTrees;
10220. 56 queueTrees = undefined;
10221. 56
10222. 56// Concat queueTrees to nextTrees.
10223. 56
10224. 56 } else {
10225. 56 if (nextOffset === queueOffset) {
10226. 56 queueTrees.forEach(function (tree) {
10227. 56 nextTrees.push(tree);
10228. 56 });
10229. 56 queueTrees = undefined;
10230. 56 }
10231. 56 queueListIi += 1;
10232. 56 }
10233. 639
10234. 639// Reached end of queueList.
10235. 639
10236. 265 if (nextOffset === undefined) {
10237. 265 if (openRange !== undefined) {
10238. 265
10239. 265// Append nested-children from parentToChildDict (within openRange) to
10240. 265// resultChildren.
10241. 265
10242. 265 resultAppendNextChild();
10243. 265 }
10244. 265 return true;
10245. 374 }
10246. 374 if (openRange !== undefined && openRange.end <= nextOffset) {
10247. 129
10248. 129// Append nested-children from parentToChildDict (within openRange) to
10249. 129// resultChildren.
10250. 129
10251. 129 resultAppendNextChild();
10252. 129 openRange = undefined;
10253. 374 }
10254. 374 if (openRange === undefined) {
10255. 292 openRangeEnd = nextOffset + 1;
10256. 502 nextTrees.forEach(function ({
10257. 502 parentIi,
10258. 502 tree
10259. 502 }) {
10260. 502 openRangeEnd = Math.max(openRangeEnd, tree.end);
10261. 502
10262. 502// Append children from nextTrees to parentToChildDict.
10263. 502
10264. 502 dictKeyValueAppend(parentToChildDict, parentIi, tree);
10265. 502 });
10266. 292 queueOffset = openRangeEnd;
10267. 292 openRange = {
10268. 292 end: openRangeEnd,
10269. 292 start: nextOffset
10270. 292 };
10271. 292 } else {
10272. 114 nextTrees.forEach(function ({
10273. 114 parentIi,
10274. 114 tree
10275. 114 }) {
10276. 114 let right;
10277. 82 if (tree.end > openRange.end) {
10278. 82 right = treeSplit(tree, openRange.end);
10279. 82 if (queueTrees === undefined) {
10280. 82 queueTrees = [];
10281. 82 }
10282. 82
10283. 82// new RangeTreeWithParent().
10284. 82
10285. 82 queueTrees.push({
10286. 82 parentIi,
10287. 82 tree: right
10288. 82 });
10289. 82 }
10290. 114
10291. 114// Append children from nextTrees to parentToChildDict.
10292. 114
10293. 114 dictKeyValueAppend(parentToChildDict, parentIi, tree);
10294. 114 });
10295. 82 }
10296. 639 }
10297. 292 function resultAppendNextChild() {
10298. 292
10299. 292// This function will append next child to <resultChildren>.
10300. 292
10301. 292 let treesMatching = [];
10302. 589 parentToChildDict.forEach(function (nested) {
10303. 589 if (
10304. 589 nested.length === 1
10305. 563 && nested[0].start === openRange.start
10306. 480 && nested[0].end === openRange.end
10307. 468 ) {
10308. 468 treesMatching.push(nested[0]);
10309. 468 } else {
10310. 121
10311. 121// new rangeTreeCreate().
10312. 121
10313. 121 treesMatching.push({
10314. 121 children: nested,
10315. 121 delta: 0,
10316. 121 end: openRange.end,
10317. 121 start: openRange.start
10318. 121 });
10319. 121 }
10320. 589 });
10321. 292 parentToChildDict.clear();
10322. 292
10323. 292// Recurse mergeTreeList().
10324. 292
10325. 292 resultChildren.push(mergeTreeList(treesMatching));
10326. 292 }
10327. 75 function treeSplit(tree, offset) {
10328. 75
10329. 75// This function will split <tree> along <offset> and return the right-side.
10330. 75// @precondition `tree.start < offset && offset < tree.end`
10331. 75// @return RangeTree Right part
10332. 75
10333. 75 let child;
10334. 75 let ii = 0;
10335. 75 let leftChildLen = tree.children.length;
10336. 75 let mid;
10337. 75 let resultTree;
10338. 75 let rightChildren;
10339. 75
10340. 75// TODO(perf): Binary search (check overhead) //jslint-ignore-line
10341. 75
10342. 19 while (ii < tree.children.length) {
10343. 19 child = tree.children[ii];
10344. 19 if (child.start < offset && offset < child.end) {
10345. 19
10346. 19// Recurse treeSplit().
10347. 19
10348. 19 mid = treeSplit(child, offset);
10349. 19 leftChildLen = ii + 1;
10350. 19 break;
10351. 19 }
10352. 19 if (child.start >= offset) {
10353. 19 leftChildLen = ii;
10354. 19 break;
10355. 19 }
10356. 19 ii += 1;
10357. 19 }
10358. 75 rightChildren = tree.children.splice(
10359. 75 leftChildLen,
10360. 75 tree.children.length - leftChildLen
10361. 75 );
10362. 4 if (mid !== undefined) {
10363. 4 rightChildren.unshift(mid);
10364. 4 }
10365. 75
10366. 75// new rangeTreeCreate().
10367. 75
10368. 75 resultTree = {
10369. 75 children: rightChildren,
10370. 75 delta: tree.delta,
10371. 75 end: tree.end,
10372. 75 start: offset
10373. 75 };
10374. 75 tree.end = offset;
10375. 75 return resultTree;
10376. 75 }
10377. 265
10378. 265// Init startToTreeDict.
10379. 265
10380. 669 parentTrees.forEach(function (parentTree, parentIi) {
10381. 545 parentTree.children.forEach(function (child) {
10382. 545
10383. 545// Append child with child.start to startToTreeDict.
10384. 545
10385. 545 dictKeyValueAppend(startToTreeDict, child.start, {
10386. 545 parentIi,
10387. 545 tree: child
10388. 545 });
10389. 545 });
10390. 669 });
10391. 265
10392. 265// init queueList.
10393. 265
10394. 335 queueList = Array.from(startToTreeDict).map(function ([
10395. 335 startOffset, trees
10396. 335 ]) {
10397. 335
10398. 335// new StartEvent().
10399. 335
10400. 335 return [
10401. 335 startOffset, trees
10402. 335 ];
10403. 217 }).sort(function (aa, bb) {
10404. 217 return aa[0] - bb[0];
10405. 217 });
10406. 639 while (true) {
10407. 639 if (nextXxx()) {
10408. 639 break;
10409. 639 }
10410. 639 }
10411. 265 return resultChildren;
10412. 265 }
10413. 97
10414. 689 function sortFunc(funcCov) {
10415. 689
10416. 689// This function will normalize-and-sort <funcCov>.ranges.
10417. 689// Sorts the ranges (pre-order sort).
10418. 689// TODO: Tree-based normalization of the ranges. //jslint-ignore-line
10419. 689// @param funcCov Function coverage to normalize.
10420. 689
10421. 689 funcCov.ranges = treeToRanges(treeFromSortedRanges(
10422. 689 funcCov.ranges.sort(compareRangeList)
10423. 689 ));
10424. 689 return funcCov;
10425. 689 }
10426. 97
10427. 129 function sortScript(scriptCov) {
10428. 129
10429. 129// This function will normalize-and-sort <scriptCov>.functions.
10430. 129
10431. 129// Normalize-and-sort functions[xxx].ranges.
10432. 129
10433. 688 scriptCov.functions.forEach(function (funcCov) {
10434. 688 sortFunc(funcCov);
10435. 688 });
10436. 129
10437. 129// Sort functions by root range (pre-order sort).
10438. 129
10439. 559 scriptCov.functions.sort(function (aa, bb) {
10440. 559 return compareRangeList(aa.ranges[0], bb.ranges[0]);
10441. 559 });
10442. 129 return scriptCov;
10443. 129 }
10444. 97
10445. 888 function treeFromSortedRanges(ranges) {
10446. 888
10447. 888// @precondition `ranges` are well-formed and pre-order sorted
10448. 888
10449. 888 let root;
10450. 888 let stack = []; // Stack of parent trees and parent counts.
10451. 1856 ranges.forEach(function (range) {
10452. 1856
10453. 1856// new rangeTreeCreate().
10454. 1856
10455. 1856 let node = {
10456. 1856 children: [],
10457. 1856 delta: range.count,
10458. 1856 end: range.endOffset,
10459. 1856 start: range.startOffset
10460. 1856 };
10461. 1856 let parent;
10462. 1856 let parentCount;
10463. 888 if (root === undefined) {
10464. 888 root = node;
10465. 888 stack.push([
10466. 888 node, range.count
10467. 888 ]);
10468. 888 return;
10469. 968 }
10470. 1565 while (true) {
10471. 1565 [
10472. 1565 parent, parentCount
10473. 1565 ] = stack[stack.length - 1];
10474. 1565
10475. 1565// assert: `top !== undefined` (the ranges are sorted)
10476. 1565
10477. 1565 if (range.startOffset < parent.end) {
10478. 1565 break;
10479. 1565 }
10480. 1565 stack.pop();
10481. 1565 }
10482. 968 node.delta -= parentCount;
10483. 968 parent.children.push(node);
10484. 968 stack.push([
10485. 968 node, range.count
10486. 968 ]);
10487. 968 });
10488. 888 return root;
10489. 888 }
10490. 97
10491. 781 function treeToRanges(tree) {
10492. 781
10493. 781// Get the range coverages corresponding to the tree.
10494. 781// The ranges are pre-order sorted.
10495. 781
10496. 781 let count;
10497. 781 let cur;
10498. 781 let ii;
10499. 781 let parentCount;
10500. 781 let ranges = [];
10501. 781 let stack = [ // Stack of parent trees and counts.
10502. 781 [
10503. 781 tree, 0
10504. 781 ]
10505. 781 ];
10506. 1630 function normalizeRange(tree) {
10507. 1630
10508. 1630// @internal
10509. 1630
10510. 1630 let children = [];
10511. 1630 let curEnd;
10512. 1630 let head;
10513. 1630 let tail = [];
10514. 849 function endChain() {
10515. 18 if (tail.length !== 0) {
10516. 18 head.end = tail[tail.length - 1].end;
10517. 18 tail.forEach(function (tailTree) {
10518. 18 tailTree.children.forEach(function (subChild) {
10519. 18 subChild.delta += tailTree.delta - head.delta;
10520. 18 head.children.push(subChild);
10521. 18 });
10522. 18 });
10523. 18 tail.length = 0;
10524. 18 }
10525. 849
10526. 849// Recurse normalizeRange().
10527. 849
10528. 849 normalizeRange(head);
10529. 849 children.push(head);
10530. 849 }
10531. 867 tree.children.forEach(function (child) {
10532. 432 if (head === undefined) {
10533. 432 head = child;
10534. 435 } else if (
10535. 435 child.delta === head.delta && child.start === curEnd
10536. 435 ) {
10537. 435 tail.push(child);
10538. 435 } else {
10539. 435 endChain();
10540. 435 head = child;
10541. 435 }
10542. 867 curEnd = child.end;
10543. 867 });
10544. 432 if (head !== undefined) {
10545. 432 endChain();
10546. 432 }
10547. 238 if (children.length === 1) {
10548. 238 if (
10549. 238 children[0].start === tree.start
10550. 238 && children[0].end === tree.end
10551. 238 ) {
10552. 238 tree.delta += children[0].delta;
10553. 238 tree.children = children[0].children;
10554. 238
10555. 238// `.lazyCount` is zero for both (both are after normalization)
10556. 238
10557. 238 return;
10558. 238 }
10559. 1624 }
10560. 1624 tree.children = children;
10561. 1624 }
10562. 781 normalizeRange(tree);
10563. 1624 while (stack.length > 0) {
10564. 1624 [
10565. 1624 cur, parentCount
10566. 1624 ] = stack.pop();
10567. 1624 count = parentCount + cur.delta;
10568. 1624 ranges.push({
10569. 1624 count,
10570. 1624 endOffset: cur.end,
10571. 1624 startOffset: cur.start
10572. 1624 });
10573. 1624 ii = cur.children.length - 1;
10574. 1624 while (ii >= 0) {
10575. 1624 stack.push([
10576. 1624 cur.children[ii], count
10577. 1624 ]);
10578. 1624 ii -= 1;
10579. 1624 }
10580. 1624 }
10581. 781 return ranges;
10582. 781 }
10583. 97
10584. 1 if (processCovs.length === 0) {
10585. 1 return {
10586. 1 result: []
10587. 1 };
10588. 96 }
10589. 96
10590. 96// Init urlToScriptDict.
10591. 96
10592. 234 processCovs.forEach(function ({
10593. 234 result
10594. 234 }) {
10595. 269 result.forEach(function (scriptCov) {
10596. 269 dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
10597. 269 });
10598. 234 });
10599. 129 urlToScriptDict.forEach(function (scriptCovs) {
10600. 129
10601. 129// assert: `scriptCovs.length > 0`
10602. 129
10603. 129// function mergeScriptList(scriptCovs) {
10604. 129// Merges a list of matching script coverages.
10605. 129// Scripts are matching if they have the same `url`.
10606. 129// The result is normalized.
10607. 129// The input values may be mutated, it is not safe to use them after passing
10608. 129// them to this function.
10609. 129// The computation is synchronous.
10610. 129// @param scriptCovs Process coverages to merge.
10611. 129// @return Merged script coverage, or `undefined` if the input list was empty.
10612. 129
10613. 129 let functions = [];
10614. 129
10615. 129// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
10616. 129
10617. 129 let rangeToFuncDict = new Map();
10618. 129
10619. 129// Probably deadcode.
10620. 129// if (scriptCovs.length === 0) {
10621. 129// return undefined;
10622. 129// }
10623. 129
10624. 96 if (scriptCovs.length === 1) {
10625. 96 resultMerged.push(sortScript(scriptCovs[0]));
10626. 96 return;
10627. 96 }
10628. 96
10629. 96// Init rangeToFuncDict.
10630. 96// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
10631. 96
10632. 226 scriptCovs.forEach(function ({
10633. 226 functions
10634. 226 }) {
10635. 277 functions.forEach(function (funcCov) {
10636. 277 dictKeyValueAppend(
10637. 277 rangeToFuncDict,
10638. 277
10639. 277// This string can be used to match function with same root range.
10640. 277// The string is derived from the start and end offsets of the root range of
10641. 277// the function.
10642. 277// This assumes that `ranges` is non-empty (true for valid function coverages).
10643. 277
10644. 277 (
10645. 277 funcCov.ranges[0].startOffset
10646. 277 + ";" + funcCov.ranges[0].endOffset
10647. 277 ),
10648. 277 funcCov
10649. 277 );
10650. 277 });
10651. 226 });
10652. 112 rangeToFuncDict.forEach(function (funcCovs) {
10653. 112
10654. 112// assert: `funcCovs.length > 0`
10655. 112
10656. 112// function mergeFuncList(funcCovs) {
10657. 112// Merges a list of matching function coverages.
10658. 112// Functions are matching if their root ranges have the same span.
10659. 112// The result is normalized.
10660. 112// The input values may be mutated, it is not safe to use them after passing
10661. 112// them to this function.
10662. 112// The computation is synchronous.
10663. 112// @param funcCovs Function coverages to merge.
10664. 112// @return Merged function coverage, or `undefined` if the input list was empty.
10665. 112
10666. 112 let count = 0;
10667. 112 let isBlockCoverage;
10668. 112 let merged;
10669. 112 let ranges;
10670. 112 let trees = [];
10671. 112
10672. 112// Probably deadcode.
10673. 112// if (funcCovs.length === 0) {
10674. 112// return undefined;
10675. 112// }
10676. 112
10677. 96 if (funcCovs.length === 1) {
10678. 96 functions.push(sortFunc(funcCovs[0]));
10679. 96 return;
10680. 111 }
10681. 111
10682. 111// assert: `funcCovs[0].ranges.length > 0`
10683. 111
10684. 276 funcCovs.forEach(function (funcCov) {
10685. 276
10686. 276// assert: `funcCov.ranges.length > 0`
10687. 276// assert: `funcCov.ranges` is sorted
10688. 276
10689. 276 count += (
10690. 276 funcCov.count !== undefined
10691. 111 ? funcCov.count
10692. 274 : funcCov.ranges[0].count
10693. 276 );
10694. 199 if (funcCov.isBlockCoverage) {
10695. 199 trees.push(treeFromSortedRanges(funcCov.ranges));
10696. 199 }
10697. 276 });
10698. 111 if (trees.length > 0) {
10699. 96 isBlockCoverage = true;
10700. 96 ranges = treeToRanges(mergeTreeList(trees));
10701. 96 } else {
10702. 96 isBlockCoverage = false;
10703. 96 ranges = [
10704. 96 {
10705. 96 count,
10706. 96 endOffset: funcCovs[0].ranges[0].endOffset,
10707. 96 startOffset: funcCovs[0].ranges[0].startOffset
10708. 96 }
10709. 96 ];
10710. 111 }
10711. 111 merged = {
10712. 111 functionName: funcCovs[0].functionName,
10713. 111 isBlockCoverage,
10714. 111 ranges
10715. 111 };
10716. 111 if (count !== ranges[0].count) {
10717. 96 merged.count = count;
10718. 111 }
10719. 111
10720. 111// assert: `merged` is normalized
10721. 111
10722. 111 functions.push(merged);
10723. 111 });
10724. 96 resultMerged.push(sortScript({
10725. 96 functions,
10726. 96 scriptId: scriptCovs[0].scriptId,
10727. 96 url: scriptCovs[0].url
10728. 96 }));
10729. 96 });
10730. 96
10731. 96// Sorts the scripts alphabetically by `url`.
10732. 96// Reassigns script ids: the script at index `0` receives `"0"`, the script at
10733. 96// index `1` receives `"1"` etc.
10734. 96
10735. 96 Object.entries(resultMerged.sort(function (aa, bb) {
10736. 96 return (
10737. 96 aa.url > bb.url
10738. 96 ? 1
10739. 96 : -1
10740. 96 );
10741. 129 })).forEach(function ([
10742. 129 scriptId, scriptCov
10743. 129 ]) {
10744. 129 scriptCov.scriptId = scriptId.toString(10);
10745. 129 });
10746. 96 return {
10747. 96 result: resultMerged
10748. 96 };
10749. 96}
10750. 1
10751. 8async function v8CoverageReportCreate({
10752. 8 consoleError,
10753. 8 coverageDir,
10754. 8 processArgv = []
10755. 8}) {
10756. 8
10757. 8// This function will create html-coverage-reports directly from
10758. 8// v8-coverage-files in <coverageDir>.
10759. 8// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
10760. 8// 2. Merge JSON v8-coverage-files in <coverageDir>.
10761. 8// 3. Create html-coverage-reports in <coverageDir>.
10762. 8
10763. 8 let cwd;
10764. 8 let excludeList = [];
10765. 8 let exitCode = 0;
10766. 8 let fileDict;
10767. 8 let includeList = [];
10768. 8 let modeIncludeNodeModules;
10769. 8 let processArgElem;
10770. 8 let promiseList = [];
10771. 8 let v8CoverageObj;
10772. 8
10773. 13 function htmlRender({
10774. 13 fileList,
10775. 13 lineList,
10776. 13 modeIndex,
10777. 13 pathname
10778. 13 }) {
10779. 13 let html;
10780. 13 let padLines;
10781. 13 let padPathname;
10782. 13 let txt;
10783. 13 let txtBorder;
10784. 13 html = "";
10785. 13 html += String(`
10786. 13<!DOCTYPE html>
10787. 13<html lang="en">
10788. 13<head>
10789. 13<title>V8 Coverage Report</title>
10790. 13<style>
10791. 13/* jslint utility2:true */
10792. 13/*csslint ignore:start*/
10793. 13.coverage,
10794. 13.coverage a,
10795. 13.coverage div,
10796. 13.coverage pre,
10797. 13.coverage span,
10798. 13.coverage table,
10799. 13.coverage tbody,
10800. 13.coverage td,
10801. 13.coverage th,
10802. 13.coverage thead,
10803. 13.coverage tr {
10804. 13 box-sizing: border-box;
10805. 13 font-family: monospace;
10806. 13}
10807. 13/*csslint ignore:end*/
10808. 13
10809. 13/* css - coverage_report - general */
10810. 13body {
10811. 13 margin: 0;
10812. 13}
10813. 13.coverage pre {
10814. 13 margin: 5px 0;
10815. 13}
10816. 13.coverage table {
10817. 13 border-collapse: collapse;
10818. 13}
10819. 13.coverage td,
10820. 13.coverage th {
10821. 13 border: 1px solid #777;
10822. 13 line-height: 20px;
10823. 13 margin: 0;
10824. 13 padding: 5px 10px;
10825. 13}
10826. 13.coverage td span {
10827. 13 display: inline-block;
10828. 13 width: 100%;
10829. 13}
10830. 13.coverage .content {
10831. 13 padding: 0 5px;
10832. 13}
10833. 13.coverage .content a {
10834. 13 text-decoration: none;
10835. 13}
10836. 13.coverage .count {
10837. 13 margin: 0 5px;
10838. 13 padding: 0 5px;
10839. 13}
10840. 13.coverage .footer,
10841. 13.coverage .header {
10842. 13 padding: 20px;
10843. 13}
10844. 13.coverage .footer {
10845. 13 text-align: center;
10846. 13}
10847. 13.coverage .percentbar {
10848. 13 height: 12px;
10849. 13 margin: 2px 0;
10850. 13 min-width: 200px;
10851. 13 position: relative;
10852. 13 width: 100%;
10853. 13}
10854. 13.coverage .percentbar div {
10855. 13 height: 100%;
10856. 13 position: absolute;
10857. 13}
10858. 13.coverage .title {
10859. 13 font-size: large;
10860. 13 font-weight: bold;
10861. 13 margin-bottom: 10px;
10862. 13}
10863. 13
10864. 13/* css - coverage_report - color */
10865. 13.coverage td,
10866. 13.coverage th {
10867. 13 background: #fff;
10868. 13}
10869. 13.coverage .count,
10870. 13.coverage .coverageHigh {
10871. 13 background: #9d9;
10872. 13}
10873. 13.coverage .count {
10874. 13 color: #666;
10875. 13}
10876. 13.coverage .coverageIgnore {
10877. 13 background: #ccc;
10878. 13}
10879. 13.coverage .coverageLow,
10880. 13.coverage .uncovered {
10881. 13 background: #ebb;
10882. 13}
10883. 13.coverage .coverageMedium {
10884. 13 background: #fd7;
10885. 13}
10886. 13.coverage .footer,
10887. 13.coverage .header,
10888. 13.coverage .lineno {
10889. 13 background: #ddd;
10890. 13}
10891. 13.coverage .percentbar {
10892. 13 background: #999;
10893. 13}
10894. 13.coverage .percentbar div {
10895. 13 background: #666;
10896. 13}
10897. 13
10898. 13/* css - coverage_report - important */
10899. 13.coverage pre:hover span,
10900. 13.coverage tr:hover td {
10901. 13 background: #7d7;
10902. 13}
10903. 13.coverage pre:hover span.uncovered,
10904. 13.coverage tr:hover td.coverageLow {
10905. 13 background: #f99;
10906. 13}
10907. 13</style>
10908. 13</head>
10909. 13<body class="coverage">
10910. 13<!-- header start -->
10911. 13<div class="header">
10912. 13<div class="title">V8 Coverage Report</div>
10913. 13<table>
10914. 13<thead>
10915. 13 <tr>
10916. 13 <th>Files covered</th>
10917. 13 <th>Lines</th>
10918. 13 <th>Remaining</th>
10919. 13 </tr>
10920. 13</thead>
10921. 13<tbody>
10922. 13 `).trim() + "\n";
10923. 7 if (modeIndex) {
10924. 7 padLines = String("(ignore) 100.00 %").length;
10925. 7 padPathname = 32;
10926. 7 fileList.unshift({
10927. 7 linesCovered: 0,
10928. 7 linesTotal: 0,
10929. 7 modeCoverageIgnoreFile: "",
10930. 7 pathname: "./"
10931. 7 });
10932. 7 fileList.slice(1).forEach(function ({
10933. 7 linesCovered,
10934. 7 linesTotal,
10935. 7 modeCoverageIgnoreFile,
10936. 7 pathname
10937. 7 }) {
10938. 7 if (!modeCoverageIgnoreFile) {
10939. 7 fileList[0].linesCovered += linesCovered;
10940. 7 fileList[0].linesTotal += linesTotal;
10941. 7 }
10942. 7 padPathname = Math.max(padPathname, pathname.length + 2);
10943. 7 padLines = Math.max(
10944. 7 padLines,
10945. 7 String(linesCovered + " / " + linesTotal).length
10946. 7 );
10947. 7 });
10948. 7 }
10949. 13 txtBorder = (
10950. 13 "+" + "-".repeat(padPathname + 2) + "+"
10951. 13 + "-".repeat(padLines + 2) + "+"
10952. 13 + "-".repeat(padLines + 2) + "+\n"
10953. 13 );
10954. 13 txt = "";
10955. 13 txt += "V8 Coverage Report\n";
10956. 13 txt += txtBorder;
10957. 13 txt += (
10958. 13 "| " + String("Files covered").padEnd(padPathname, " ") + " | "
10959. 13 + String("Lines").padStart(padLines, " ") + " | "
10960. 13 + String("Remaining").padStart(padLines, " ") + " |\n"
10961. 13 );
10962. 13 txt += txtBorder;
10963. 19 fileList.forEach(function ({
10964. 19 linesCovered,
10965. 19 linesTotal,
10966. 19 modeCoverageIgnoreFile,
10967. 19 pathname
10968. 19 }, ii) {
10969. 19 let coverageLevel;
10970. 19 let coveragePct;
10971. 19 let fill;
10972. 19 let str1;
10973. 19 let str2;
10974. 19 let xx1;
10975. 19 let xx2;
10976. 2 coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
10977. 19 coverageLevel = (
10978. 19 modeCoverageIgnoreFile
10979. 2 ? "coverageIgnore"
10980. 17 : coveragePct >= 8000
10981. 17 ? "coverageHigh"
10982. 17 : coveragePct >= 5000
10983. 17 ? "coverageMedium"
10984. 17 : "coverageLow"
10985. 19 );
10986. 19 coveragePct = String(coveragePct).replace((
10987. 19 /..$/m
10988. 19 ), ".$&");
10989. 13 if (modeIndex && ii === 0) {
10990. 7 fill = (
10991. 7
10992. 7// Badge-color rgb-red.
10993. 7
10994. 7 "#" + Math.round(
10995. 7 (100 - Number(coveragePct)) * 2.21
10996. 7 ).toString(16).padStart(2, "0")
10997. 7
10998. 7// Badge-color rgb-green.
10999. 7
11000. 7 + Math.round(
11001. 7 Number(coveragePct) * 2.21
11002. 7 ).toString(16).padStart(2, "0")
11003. 7
11004. 7// Badge-color rgb-blue.
11005. 7
11006. 7 + "00"
11007. 7 );
11008. 7 str1 = "coverage";
11009. 7 str2 = coveragePct + " %";
11010. 7 xx1 = 6 * str1.length + 20;
11011. 7 xx2 = 6 * str2.length + 20;
11012. 7
11013. 7// Fs - write coverage_badge.svg.
11014. 7
11015. 7 promiseList.push(fsWriteFileWithParents((
11016. 7 coverageDir + "coverage_badge.svg"
11017. 7 ), String(`
11018. 7<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
11019. 7<rect fill="#555" height="20" width="${xx1 + xx2}"/>
11020. 7<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
11021. 7<g
11022. 7 fill="#fff"
11023. 7 font-family="verdana, geneva, dejavu sans, sans-serif"
11024. 7 font-size="11"
11025. 7 font-weight="bold"
11026. 7 text-anchor="middle"
11027. 7>
11028. 7<text x="${0.5 * xx1}" y="14">${str1}</text>
11029. 7<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
11030. 7</g>
11031. 7</svg>
11032. 7 `).trim() + "\n"));
11033. 7 pathname = "";
11034. 7 }
11035. 19 txt += (
11036. 19 "| "
11037. 19 + String("./" + pathname).padEnd(padPathname, " ") + " | "
11038. 19 + String(
11039. 19 modeCoverageIgnoreFile + " " + coveragePct + " %"
11040. 19 ).padStart(padLines, " ") + " | "
11041. 19 + " ".repeat(padLines) + " |\n"
11042. 19 );
11043. 19 txt += (
11044. 19 "| " + "*".repeat(
11045. 19 Math.round(0.01 * coveragePct * padPathname)
11046. 19 ).padEnd(padPathname, "_") + " | "
11047. 19 + String(
11048. 19 linesCovered + " / " + linesTotal
11049. 19 ).padStart(padLines, " ") + " | "
11050. 19 + String(
11051. 19 (linesTotal - linesCovered) + " / " + linesTotal
11052. 19 ).padStart(padLines, " ") + " |\n"
11053. 19 );
11054. 19 txt += txtBorder;
11055. 19 pathname = htmlEscape(pathname);
11056. 19
11057. 19// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
11058. 19
11059. 19 html += String(`
11060. 19 <tr>
11061. 19 <td class="${coverageLevel}">
11062. 19 ${(
11063. 19 modeIndex
11064. 13 ? (
11065. 13 "<a href=\"" + (pathname || "index") + ".html\">. / "
11066. 13 + pathname + "</a><br>"
11067. 13 )
11068. 6 : (
11069. 6 "<a href=\""
11070. 6 + "../".repeat(pathname.split("/").length - 1)
11071. 6 + "index.html\">. / </a>"
11072. 6 + pathname + "<br>"
11073. 6 )
11074. 19 )}
11075. 19 <div class="percentbar">
11076. 19 <div style="width: ${coveragePct}%;"></div>
11077. 19 </div>
11078. 19 </td>
11079. 19 <td style="text-align: right;">
11080. 19 ${modeCoverageIgnoreFile} ${coveragePct} %<br>
11081. 19 ${linesCovered} / ${linesTotal}
11082. 19 </td>
11083. 19 <td style="text-align: right;">
11084. 19 <br>
11085. 19 ${linesTotal - linesCovered} / ${linesTotal}
11086. 19 </td>
11087. 19 </tr>
11088. 19 `).trim() + "\n";
11089. 19 });
11090. 13 html += String(`
11091. 13</tbody>
11092. 13</table>
11093. 13</div>
11094. 13<!-- header end -->
11095. 13 `).trim() + "\n";
11096. 6 if (!modeIndex) {
11097. 6 html += String(`
11098. 6<!-- content start -->
11099. 6<div class="content">
11100. 6 `).trim() + "\n";
11101. 11829 lineList.forEach(function ({
11102. 11829 count,
11103. 11829 holeList,
11104. 11829 line,
11105. 11829 startOffset
11106. 11829 }, ii) {
11107. 11829 let chunk;
11108. 11829 let inHole;
11109. 11829 let lineHtml;
11110. 11829 let lineId;
11111. 11829 lineHtml = "";
11112. 11829 lineId = "line_" + (ii + 1);
11113. 11829 switch (count) {
11114. 32 case -1:
11115. 11195 case 0:
11116. 11195 if (holeList.length === 0) {
11117. 11195 lineHtml += "</span>";
11118. 11195 lineHtml += "<span class=\"uncovered\">";
11119. 11195 lineHtml += htmlEscape(line);
11120. 11195 break;
11121. 11195 }
11122. 11195 line = line.split("").map(function (char) {
11123. 11195 return {
11124. 11195 char,
11125. 11195 isHole: undefined
11126. 11195 };
11127. 11195 });
11128. 11195 holeList.forEach(function ([
11129. 11195 aa, bb
11130. 11195 ]) {
11131. 11195 aa = Math.max(aa - startOffset, 0);
11132. 11195 bb = Math.min(bb - startOffset, line.length);
11133. 11195 while (aa < bb) {
11134. 11195 line[aa].isHole = true;
11135. 11195 aa += 1;
11136. 11195 }
11137. 11195 });
11138. 11195 chunk = "";
11139. 11195 line.forEach(function ({
11140. 11195 char,
11141. 11195 isHole
11142. 11195 }) {
11143. 11195 if (inHole !== isHole) {
11144. 11195 lineHtml += htmlEscape(chunk);
11145. 11195 lineHtml += "</span><span";
11146. 11195
11147. 11195// Coverage-hack - Ugly-hack around possible deadcode where isHole is always
11148. 11195// true.
11149. 11195
11150. 11195 if (isHole) {
11151. 11195 lineHtml += " class=\"uncovered\"";
11152. 11195 }
11153. 11195 lineHtml += ">";
11154. 11195 chunk = "";
11155. 11195 inHole = isHole;
11156. 11195 }
11157. 11195 chunk += char;
11158. 11195 });
11159. 11195 lineHtml += htmlEscape(chunk);
11160. 11195 break;
11161. 634 default:
11162. 634 lineHtml += htmlEscape(line);
11163. 11829 }
11164. 11829 html += String(`
11165. 11829<pre>
11166. 11829<span class="lineno">
11167. 11829<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
11168. 11829</span>
11169. 11829<span class="count
11170. 11829 ${(
11171. 11829 count <= 0
11172. 11195 ? "uncovered"
11173. 634 : ""
11174. 11829 )}"
11175. 11829>
11176. 11163${String(count || "-0").padStart(7, " ")}
11177. 11829</span>
11178. 11829<span>${lineHtml}</span>
11179. 11829</pre>
11180. 11829 `).replace((
11181. 11829 /\n/g
11182. 11829 ), "").trim() + "\n";
11183. 11829 });
11184. 6 html += String(`
11185. 6</div>
11186. 6<!-- content end -->
11187. 6 `).trim() + "\n";
11188. 6 }
11189. 13 html += String(`
11190. 13<div class="footer">
11191. 13 [
11192. 13 This document was created with
11193. 13 <a href="https://github.com/jslint-org/jslint">JSLint</a>
11194. 13 ]
11195. 13</div>
11196. 13</body>
11197. 13</html>
11198. 13 `).trim() + "\n";
11199. 13
11200. 13// Fs - write <file>.html.
11201. 13
11202. 13 promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
11203. 6 if (!modeIndex) {
11204. 6 return;
11205. 7 }
11206. 7
11207. 7// Fs - write coverage_report.txt.
11208. 7
11209. 7 consoleError("\n" + txt);
11210. 7 promiseList.push(fsWriteFileWithParents((
11211. 7 coverageDir + "coverage_report.txt"
11212. 7 ), txt));
11213. 7 }
11214. 8
11215. 8/*
11216. 8function sentinel() {}
11217. 8*/
11218. 8
11219. 8 await moduleFsInit();
11220. 1 consoleError = consoleError || console.error;
11221. 8 cwd = process.cwd().replace((
11222. 8 /\\/g
11223. 8 ), "/") + "/";
11224. 8
11225. 8// Init coverageDir.
11226. 8// Assert coverageDir is subdirectory of cwd.
11227. 8
11228. 8 assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
11229. 8
11230. 8// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
11231. 8// assertOrThrow(
11232. 8// pathnameRelativeCwd(coverageDir),
11233. 8// "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
11234. 8// );
11235. 8
11236. 8 coverageDir = modulePath.resolve(coverageDir).replace((
11237. 8 /\\/g
11238. 8 ), "/") + "/";
11239. 8
11240. 8 processArgv = processArgv.slice();
11241. 9 while (processArgv[0] && processArgv[0][0] === "-") {
11242. 3 processArgElem = processArgv.shift().split("=");
11243. 3 processArgElem[1] = processArgElem.slice(1).join("=");
11244. 3 switch (processArgElem[0]) {
11245. 3
11246. 3// PR-371 - Add cli-option `--exclude=...`.
11247. 3
11248. 3 case "--exclude":
11249. 3 excludeList.push(processArgElem[1]);
11250. 3 break;
11251. 3
11252. 3// PR-371 - Add cli-option `--include=...`
11253. 3
11254. 3 case "--include":
11255. 3 includeList.push(processArgElem[1]);
11256. 3 break;
11257. 3
11258. 3// PR-400
11259. 3// Disable default-coverage of directory `node_modules`,
11260. 3// but allow override with cli-option `--include-node-modules=1`.
11261. 3
11262. 3 case "--include-node-modules":
11263. 3 modeIncludeNodeModules = !(
11264. 3 /0|false|null|undefined/
11265. 3 ).test(processArgElem[1]);
11266. 3 break;
11267. 3 }
11268. 7 }
11269. 7
11270. 7// 1. Spawn node.js program <processArgv> with coverage
11271. 7
11272. 7 if (processArgv.length > 0) {
11273. 6
11274. 6// Remove old coverage-files.
11275. 6
11276. 6 await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
11277. 6 await Promise.all(Array.from(
11278. 6 await moduleFs.promises.readdir(coverageDir)
11279. 11 ).map(async function (file) {
11280. 11 if ((
11281. 11 /^coverage-\d+?-\d+?-\d+?\.json$/
11282. 6 ).test(file)) {
11283. 6 consoleError("rm file " + coverageDir + file);
11284. 6 await moduleFs.promises.unlink(coverageDir + file);
11285. 6 }
11286. 11 }));
11287. 6 exitCode = await new Promise(function (resolve) {
11288. 6 moduleChildProcess.spawn(
11289. 6 (
11290. 6 processArgv[0] === "npm"
11291. 6
11292. 6// If win32 environment, then replace program npm with npm.cmd.
11293. 6// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
11294. 6
11295. 6 ? process.platform.replace("win32", "npm.cmd").replace(
11296. 6 process.platform,
11297. 6 "npm"
11298. 6 )
11299. 6 : processArgv[0]
11300. 6 ),
11301. 6 processArgv.slice(1),
11302. 6 {
11303. 6 env: Object.assign({}, process.env, {
11304. 6 NODE_V8_COVERAGE: coverageDir
11305. 6 }),
11306. 6 stdio: ["ignore", 1, 2]
11307. 6 }
11308. 6 ).on("exit", resolve);
11309. 6 });
11310. 6 consoleError(
11311. 6 `v8CoverageReportCreate - program exited with exitCode=${exitCode}`
11312. 6 );
11313. 7 }
11314. 7
11315. 7// 2. Merge JSON v8-coverage-files in <coverageDir>.
11316. 7
11317. 7 consoleError("v8CoverageReportCreate - merging coverage files...");
11318. 7 v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
11319. 18 v8CoverageObj = v8CoverageObj.filter(function (file) {
11320. 18 return (
11321. 18 /^coverage-\d+?-\d+?-\d+?\.json$/
11322. 18 ).test(file);
11323. 18 });
11324. 7 v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
11325. 7 let data;
11326. 7 let pathnameDict = Object.create(null);
11327. 7 data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
11328. 7 data = JSON.parse(data);
11329. 770 data.result.forEach(function (scriptCov) {
11330. 770 let pathname = scriptCov.url;
11331. 770
11332. 770// Filter out internal coverages.
11333. 770
11334. 463 if (!pathname.startsWith("file:///")) {
11335. 463 return;
11336. 463 }
11337. 307
11338. 307// Normalize pathname.
11339. 307
11340. 307 pathname = moduleUrl.fileURLToPath(pathname);
11341. 307 pathname = modulePath.resolve(pathname).replace((
11342. 307 /\\/g
11343. 307 ), "/");
11344. 307
11345. 307// Filter files outside of cwd.
11346. 307
11347. 307 if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
11348. 301 return;
11349. 301 }
11350. 7
11351. 7// Normalize pathname relative to cwd.
11352. 7
11353. 7 pathname = pathname.slice(cwd.length);
11354. 7 scriptCov.url = pathname;
11355. 7 pathnameDict[pathname] = scriptCov;
11356. 7 });
11357. 7
11358. 7// PR-400 - Filter directory `node_modules`.
11359. 7
11360. 7 if (!modeIncludeNodeModules) {
11361. 7 excludeList.push("node_modules/");
11362. 7 }
11363. 7
11364. 7// PR-400 - Filter files by glob-patterns in excludeList, includeList.
11365. 7
11366. 7 data.result = globExclude({
11367. 7 excludeList,
11368. 7 includeList,
11369. 7 pathnameList: Object.keys(pathnameDict)
11370. 7 }).pathnameList.map(function (pathname) {
11371. 7 return pathnameDict[pathname];
11372. 7 });
11373. 7 return data;
11374. 7 }));
11375. 7
11376. 7// Merge v8CoverageObj.
11377. 7
11378. 7 v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
11379. 7
11380. 7// Debug v8CoverageObj.
11381. 7
11382. 7 await fsWriteFileWithParents(
11383. 7 coverageDir + "v8_coverage_merged.json",
11384. 7 JSON.stringify(v8CoverageObj, undefined, 1)
11385. 7 );
11386. 7
11387. 7// 3. Create html-coverage-reports in <coverageDir>.
11388. 7
11389. 7 consoleError("v8CoverageReportCreate - creating html-coverage-report...");
11390. 7 fileDict = Object.create(null);
11391. 7 await Promise.all(v8CoverageObj.result.map(async function ({
11392. 7 functions,
11393. 7 url: pathname
11394. 7 }) {
11395. 7 let lineList;
11396. 7 let linesCovered;
11397. 7 let linesTotal;
11398. 7 let source;
11399. 7 source = await moduleFs.promises.readFile(pathname, "utf8");
11400. 7 lineList = [{}];
11401. 7 source.replace((
11402. 7 /^.*$/gm
11403. 11829 ), function (line, startOffset) {
11404. 11829 lineList[lineList.length - 1].endOffset = startOffset - 1;
11405. 11829 lineList.push({
11406. 11829 count: -1,
11407. 11829 endOffset: 0,
11408. 11829 holeList: [],
11409. 11829 line,
11410. 11829 startOffset
11411. 11829 });
11412. 11829 return "";
11413. 11829 });
11414. 7 lineList.shift();
11415. 7 lineList[lineList.length - 1].endOffset = source.length;
11416. 41 functions.reverse().forEach(function ({
11417. 41 ranges
11418. 41 }) {
11419. 65 ranges.reverse().forEach(function ({
11420. 65 count,
11421. 65 endOffset,
11422. 65 startOffset
11423. 65 }, ii, list) {
11424. 578847 lineList.forEach(function (elem) {
11425. 578847 if (!(
11426. 578847 (
11427. 578847 elem.startOffset <= startOffset
11428. 205552 && startOffset <= elem.endOffset
11429. 578782 ) || (
11430. 578782 elem.startOffset <= endOffset
11431. 578782 && endOffset <= elem.endOffset
11432. 578782 ) || (
11433. 578726 startOffset <= elem.startOffset
11434. 578726 && elem.endOffset <= endOffset
11435. 578726 )
11436. 555345 )) {
11437. 555345 return;
11438. 555345 }
11439. 23502
11440. 23502// Handle tree-root.
11441. 23502
11442. 23502 if (ii + 1 === list.length) {
11443. 23233 if (elem.count === -1) {
11444. 23233 elem.count = count;
11445. 23233 }
11446. 23233 return;
11447. 23233 }
11448. 269
11449. 269// Handle tree-children.
11450. 269
11451. 269 if (elem.count !== 0) {
11452. 170 elem.count = Math.max(count, elem.count);
11453. 269 }
11454. 269 if (count === 0) {
11455. 203 elem.count = 0;
11456. 203 elem.holeList.push([
11457. 203 startOffset, endOffset
11458. 203 ]);
11459. 203 }
11460. 578847 });
11461. 65 });
11462. 41 });
11463. 7 linesTotal = lineList.length;
11464. 11829 linesCovered = lineList.filter(function ({
11465. 11829 count
11466. 11829 }) {
11467. 11829 return count > 0;
11468. 11829 }).length;
11469. 7 await moduleFs.promises.mkdir((
11470. 7 modulePath.dirname(coverageDir + pathname)
11471. 7 ), {
11472. 7 recursive: true
11473. 7 });
11474. 7 fileDict[pathname] = {
11475. 7 lineList,
11476. 7 linesCovered,
11477. 7 linesTotal,
11478. 7 modeCoverageIgnoreFile: (
11479. 7 (
11480. 7 /^\/\*coverage-ignore-file\*\/$/m
11481. 7 ).test(source.slice(0, 65536))
11482. 7 ? "(ignore)"
11483. 7 : ""
11484. 7 ),
11485. 7 pathname
11486. 7 };
11487. 7 htmlRender({
11488. 7 fileList: [
11489. 7 fileDict[pathname]
11490. 7 ],
11491. 7 lineList,
11492. 7 pathname: coverageDir + pathname
11493. 7 });
11494. 7 }));
11495. 7 htmlRender({
11496. 7 fileList: Object.keys(fileDict).sort().map(function (pathname) {
11497. 7 return fileDict[pathname];
11498. 7 }),
11499. 7 modeIndex: true,
11500. 7 pathname: coverageDir + "index"
11501. 7 });
11502. 7 await Promise.all(promiseList);
11503. 7 assertOrThrow(
11504. 7 exitCode === 0,
11505. 7 "v8CoverageReportCreate - nonzero exitCode " + exitCode
11506. 7 );
11507. 7}
11508. 1
11509. 1/*
11510. 1function sentinel() {}
11511. 1*/
11512. 1
11513. 1// Export jslint as cjs/esm.
11514. 1
11515. 1jslint_export = Object.freeze(Object.assign(jslint, {
11516. 1 assertErrorThrownAsync,
11517. 1 assertJsonEqual,
11518. 1 assertOrThrow,
11519. 1 debugInline,
11520. 1 fsWriteFileWithParents,
11521. 1 globExclude,
11522. 1 htmlEscape,
11523. 1 jslint,
11524. 1 jslint_apidoc,
11525. 1 jslint_assert,
11526. 1 jslint_charset_ascii,
11527. 1 jslint_cli,
11528. 1 jslint_edition,
11529. 1 jslint_phase1_split,
11530. 1 jslint_phase2_lex,
11531. 1 jslint_phase3_parse,
11532. 1 jslint_phase4_walk,
11533. 1 jslint_phase5_whitage,
11534. 1 jslint_report,
11535. 1 jstestDescribe,
11536. 1 jstestIt,
11537. 1 jstestOnExit,
11538. 1 moduleFsInit,
11539. 1 noop,
11540. 1 objectDeepCopyWithKeysSorted,
11541. 1 v8CoverageListMerge,
11542. 1 v8CoverageReportCreate
11543. 1}));
11544. 1// module.exports = jslint_export; // Export jslint as cjs.
11545. 1export default Object.freeze(jslint_export); // Export jslint as esm.
11546. 1jslint_import_meta_url = import.meta.url;
11547. 1
11548. 1// Run jslint_cli.
11549. 1jslint_cli({});
11550. 1