From 2bd0f59d9d152851a2bc6fc587b00b01e8ec1e17 Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Mon, 2 May 2016 14:11:47 +0200 Subject: [PATCH] Bump jshint to 2.9.2 --- js/.jshintrc | 1 - js/common/bootstrap/monkeypatches.js | 26 +- js/node/node_modules/jshint/CHANGELOG.md | 1090 ++++ js/node/node_modules/jshint/README.md | 89 +- .../node_modules/jshint/dist/jshint-rhino.js | 4516 ++++++++++------- js/node/node_modules/jshint/dist/jshint.js | 4399 ++++++++++------ .../node_modules/htmlparser2/.travis.yml | 2 + .../jshint/node_modules/htmlparser2/README.md | 29 +- .../node_modules/htmlparser2/lib/Parser.js | 6 +- .../node_modules/core-util-is/LICENSE | 19 + .../node_modules/core-util-is/lib/util.js | 22 +- .../node_modules/core-util-is/package.json | 30 +- .../node_modules/core-util-is/test.js | 68 + .../node_modules/core-util-is/util.js | 106 - .../node_modules/inherits/package.json | 20 +- .../node_modules/readable-stream/package.json | 37 +- .../node_modules/readable-stream/readable.js | 3 + .../node_modules/htmlparser2/package.json | 20 +- .../test/Events/08-implicit-close-tags.json | 6 +- .../jshint/node_modules/minimatch/LICENSE | 15 + .../jshint/node_modules/minimatch/README.md | 216 + .../jshint/node_modules/minimatch/browser.js | 1159 +++++ .../node_modules/minimatch/minimatch.js | 912 ++++ .../node_modules/brace-expansion/.npmignore | 3 + .../node_modules/brace-expansion/README.md | 122 + .../node_modules/brace-expansion/example.js | 8 + .../node_modules/brace-expansion/index.js | 191 + .../node_modules/balanced-match/.npmignore | 5 + .../node_modules/balanced-match/LICENSE.md | 21 + .../node_modules/balanced-match/README.md | 91 + .../node_modules/balanced-match/index.js | 58 + .../node_modules/balanced-match/package.json | 76 + .../node_modules/concat-map/.travis.yml | 4 + .../node_modules/concat-map/LICENSE | 18 + .../node_modules/concat-map/README.markdown | 62 + .../node_modules/concat-map/example/map.js | 6 + .../node_modules/concat-map/index.js | 13 + .../node_modules/concat-map/package.json | 83 + .../node_modules/concat-map/test/map.js | 39 + .../node_modules/brace-expansion/package.json | 78 + .../node_modules/minimatch/package.json | 63 + .../node_modules/strip-json-comments/cli.js | 4 +- .../node_modules/strip-json-comments/license | 21 + .../strip-json-comments/package.json | 26 +- .../strip-json-comments/readme.md | 10 +- .../strip-json-comments.js | 20 +- js/node/node_modules/jshint/package.json | 40 +- js/node/node_modules/jshint/src/cli.js | 62 +- js/node/node_modules/jshint/src/jshint.js | 2051 ++++---- js/node/node_modules/jshint/src/lex.js | 96 +- js/node/node_modules/jshint/src/messages.js | 29 +- js/node/node_modules/jshint/src/options.js | 85 +- js/node/node_modules/jshint/src/reg.js | 2 +- .../node_modules/jshint/src/scope-manager.js | 857 ++++ js/node/node_modules/jshint/src/state.js | 122 +- js/node/node_modules/jshint/src/style.js | 9 +- js/node/node_modules/jshint/src/vars.js | 12 +- js/node/package.json | 2 +- 58 files changed, 12323 insertions(+), 4857 deletions(-) create mode 100644 js/node/node_modules/jshint/CHANGELOG.md create mode 100644 js/node/node_modules/jshint/node_modules/htmlparser2/node_modules/readable-stream/node_modules/core-util-is/LICENSE create mode 100644 js/node/node_modules/jshint/node_modules/htmlparser2/node_modules/readable-stream/node_modules/core-util-is/test.js delete mode 100644 js/node/node_modules/jshint/node_modules/htmlparser2/node_modules/readable-stream/node_modules/core-util-is/util.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/LICENSE create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/README.md create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/browser.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/minimatch.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/.npmignore create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/README.md create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/example.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/index.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.npmignore create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/LICENSE.md create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/README.md create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/index.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/package.json create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/.travis.yml create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/LICENSE create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/README.markdown create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/example/map.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/index.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/package.json create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/test/map.js create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/node_modules/brace-expansion/package.json create mode 100644 js/node/node_modules/jshint/node_modules/minimatch/package.json create mode 100644 js/node/node_modules/jshint/node_modules/strip-json-comments/license create mode 100644 js/node/node_modules/jshint/src/scope-manager.js diff --git a/js/.jshintrc b/js/.jshintrc index a37db2c80d..efdce0b4a8 100644 --- a/js/.jshintrc +++ b/js/.jshintrc @@ -5,7 +5,6 @@ "esnext": true, "forin": true, "freeze": true, - "globalstrict": true, "immed": true, "indent": 2, "laxbreak": true, diff --git a/js/common/bootstrap/monkeypatches.js b/js/common/bootstrap/monkeypatches.js index 78d2e70b57..c13f8fb987 100644 --- a/js/common/bootstrap/monkeypatches.js +++ b/js/common/bootstrap/monkeypatches.js @@ -37,9 +37,20 @@ //////////////////////////////////////////////////////////////////////////////// Object.defineProperty(Object.prototype, '_shallowCopy', { + set(value) { + if (this === Object.prototype) { + throw new TypeError(`Can not override Object.prototype._shallowCopy!`); + } + Object.defineProperty(this, '_shallowCopy', { + configurable: true, + enumerable: true, + writable: true, + value + }); + }, get() { var self = this; - return this.propertyKeys.reduce(function (previous, key) { + return Object.prototype.propertyKeys.reduce(function (previous, key) { previous[key] = self[key]; return previous; }, {}); @@ -55,7 +66,18 @@ Object.defineProperty(Object.prototype, 'propertyKeys', { return Object.keys(this).filter(function (key) { return (key.charAt(0) !== '_' && key.charAt(0) !== '$'); }); - } + }, + set(value) { + if (this === Object.prototype) { + throw new TypeError(`Can not override Object.prototype.propertyKeys!`); + } + Object.defineProperty(this, 'propertyKeys', { + configurable: true, + enumerable: true, + writable: true, + value + }); + }, }); diff --git a/js/node/node_modules/jshint/CHANGELOG.md b/js/node/node_modules/jshint/CHANGELOG.md new file mode 100644 index 0000000000..ad4f4eb571 --- /dev/null +++ b/js/node/node_modules/jshint/CHANGELOG.md @@ -0,0 +1,1090 @@ + +## [2.9.2](https://github.com/jshint/jshint/compare/2.9.1...v2.9.2) (2016-04-19) + +This release contains a number of bug fixes. As always, we thank everyone who +reported issues and submitted patches; those contributions are essential to the +continuing improvement of the project. We hope you'll keep it up! + +### Bug Fixes + +* (cli - extract) lines can end with "\\r\\n", not "\\n\\r" ([93818f3](https://github.com/jshint/jshint/commit/93818f3)), closes [#2825](https://github.com/jshint/jshint/issues/2825) +* Account for implied closures ([c3b4d63](https://github.com/jshint/jshint/commit/c3b4d63)) +* Add CompositionEvent to browser globals ([56515cf](https://github.com/jshint/jshint/commit/56515cf)) +* Allow destructuring in setter parameter ([97d0ac1](https://github.com/jshint/jshint/commit/97d0ac1)) +* Allow parentheses around object destructuring assignment. ([7a0bd70](https://github.com/jshint/jshint/commit/7a0bd70)), closes [#2775](https://github.com/jshint/jshint/issues/2775) +* Allow regex inside template literal ([5dd9c90](https://github.com/jshint/jshint/commit/5dd9c90)), closes [#2791](https://github.com/jshint/jshint/issues/2791) +* Allow regexp literal after 'instanceof' ([caa30e6](https://github.com/jshint/jshint/commit/caa30e6)), closes [#2773](https://github.com/jshint/jshint/issues/2773) +* Correct CLI's indentation offset logic ([47daf76](https://github.com/jshint/jshint/commit/47daf76)), closes [#2778](https://github.com/jshint/jshint/issues/2778) +* Do not crash on invalid input ([2e0026f](https://github.com/jshint/jshint/commit/2e0026f)) +* Do not fail on valid configurations ([2fb3c24](https://github.com/jshint/jshint/commit/2fb3c24)) +* Don't throw E056 for vars used in two functions ([fd91d4a](https://github.com/jshint/jshint/commit/fd91d4a)), closes [#2838](https://github.com/jshint/jshint/issues/2838) +* Emit correct token value from "module" API ([4a43fb9](https://github.com/jshint/jshint/commit/4a43fb9)) +* Expand forms accepted in dstr. assignment ([8bbd537](https://github.com/jshint/jshint/commit/8bbd537)) +* Improve binding power for tagged templates ([9cf2ff0](https://github.com/jshint/jshint/commit/9cf2ff0)) +* Improve reporting of "Bad assignment." ([08df19e](https://github.com/jshint/jshint/commit/08df19e)) +* Make the 'freeze' option less strict ([b76447c](https://github.com/jshint/jshint/commit/b76447c)), closes [#1600](https://github.com/jshint/jshint/issues/1600) +* Report "Bad assignment." in destructuring ([fe559ed](https://github.com/jshint/jshint/commit/fe559ed)) +* Report character position for camelcase errors ([480252a](https://github.com/jshint/jshint/commit/480252a)), closes [#2845](https://github.com/jshint/jshint/issues/2845) +* Reserve `await` keyword in ES6 module code ([b1c8d5b](https://github.com/jshint/jshint/commit/b1c8d5b)) + + + +## [2.9.1](https://github.com/jshint/jshint/compare/2.9.1-rc3...v2.9.1) (2016-01-14) + +Following the revocation of version 2.9.0, we observed an extended "release +candidate" phase where we encouraged users to vet JSHint for undesirable +changes in behavior. During that time, we identified and resolved a number of +such regressions. This release comprises all changes from the release candidate +phase along with the improvements initially released as version 2.9.0. This +release does not itself contain any changes to the codebase. If you are +upgrading from version 2.8.0 or earlier, please refer to the +previously-published release notes for details on bug fixes and features--these +can be found in the project's `CHANGELOG.md` file and on the project's website. + + + +## [2.9.1-rc3](https://github.com/jshint/jshint/compare/2.9.1-rc2...v2.9.1-rc3) (2016-01-12) + + +### Bug Fixes + +* Do not require global USD for any envs ([3fa9ece](https://github.com/jshint/jshint/commit/3fa9ece)) + + + + +## [2.9.1-rc2](https://github.com/jshint/jshint/compare/2.9.1-rc1...v2.9.1-rc2) (2015-12-22) + + +### Bug Fixes + +* Abort in the presence of invalid config ([767c47d](https://github.com/jshint/jshint/commit/767c47d)) +* Allow ignoring W020 and W021 ([46db923](https://github.com/jshint/jshint/commit/46db923)), closes [#2761](https://github.com/jshint/jshint/issues/2761) +* Correct `unused` for function-scoped vars ([91fa9fc](https://github.com/jshint/jshint/commit/91fa9fc)) +* Disallow ambiguous configuration values ([eb54a4c](https://github.com/jshint/jshint/commit/eb54a4c)) +* Do not disable ES6 when `moz` is set ([97dfd90](https://github.com/jshint/jshint/commit/97dfd90)) +* Don't throw '(NaN% scanned)' ([903b698](https://github.com/jshint/jshint/commit/903b698)) + + + + +## [2.9.1-rc1](https://github.com/jshint/jshint/compare/2.9.0...v2.9.1-rc1) (2015-11-12) + +Version 2.9.0 was revoked shortly after its release due to a number of +regressions. Although the underlying issues have been resolved, we are +sensitive to the possibility that there may be still more; as mentioned in +2.9.0's release notes, the variable tracking system saw a significant +refactoring. + +In an effort to minimize friction with a new version, we're publishing a +release candidate and requesting feedback from early adopters. Please give it a +try in your projects and let us know about any surprising behavior! + +### Bug Fixes + +* `latedef` shouldn't warn when marking a var as exported ([c630994](https://github.com/jshint/jshint/commit/c630994)), closes [#2662](https://github.com/jshint/jshint/issues/2662) +* Add `File` and `FileList` to browser global variables ([7f2a729](https://github.com/jshint/jshint/commit/7f2a729)), closes [#2690](https://github.com/jshint/jshint/issues/2690) +* Allow comments and new lines after /* falls through */ ([3b1c925](https://github.com/jshint/jshint/commit/3b1c925)), closes [#2652](https://github.com/jshint/jshint/issues/2652) [#1660](https://github.com/jshint/jshint/issues/1660) +* Allow let and const to be in a block outside of a block ([84a9145](https://github.com/jshint/jshint/commit/84a9145)), closes [#2685](https://github.com/jshint/jshint/issues/2685) +* Always warn about missing "use strict" directive ([e85c2a1](https://github.com/jshint/jshint/commit/e85c2a1)), closes [#2668](https://github.com/jshint/jshint/issues/2668) +* Disallow incompatible option values ([72ba5ad](https://github.com/jshint/jshint/commit/72ba5ad)) +* Do not enable `newcap` within strict mode ([acaf3f7](https://github.com/jshint/jshint/commit/acaf3f7)) +* Don't throw W080 when the initializer starts with `undefined` ([0d87919](https://github.com/jshint/jshint/commit/0d87919)), closes [#2699](https://github.com/jshint/jshint/issues/2699) +* Don't warn that an exported function is used before it is defined. ([d0433d2](https://github.com/jshint/jshint/commit/d0433d2)), closes [#2658](https://github.com/jshint/jshint/issues/2658) +* Enforce Identifier restrictions lazily ([ceca549](https://github.com/jshint/jshint/commit/ceca549)) +* Global "use strict" regressions ([04b43d2](https://github.com/jshint/jshint/commit/04b43d2)), closes [#2657](https://github.com/jshint/jshint/issues/2657) [#2661](https://github.com/jshint/jshint/issues/2661) +* Support property assignment when destructure assigning ([b6df1f2](https://github.com/jshint/jshint/commit/b6df1f2)), closes [#2659](https://github.com/jshint/jshint/issues/2659) [#2660](https://github.com/jshint/jshint/issues/2660) +* Throw W119 instead of "Unexpected '`'" when using templates in ES5 mode. ([87064e8](https://github.com/jshint/jshint/commit/87064e8)) + +### Features + +* Support QUnit's global notOk ([73ac9b8](https://github.com/jshint/jshint/commit/73ac9b8)) + + + + +# [2.9.0](https://github.com/jshint/jshint/compare/2.8.0...v2.9.0) (2015-09-03) + +**Note** This release was revoked shortly following its publication. Please +refer to the release notes for version 2.9.1 for more information (found in the +project's `CHANGELOG.md` file and on the project's website). + +This release was a long time in the making, but it may not be the most exciting +version we've published. Most of the changes are internal refactorings that +were necessary to properly fix bugs. And fix bugs we did! Special thanks go to +Luke Page, the newest addition to the JSHint team. Luke is a maintainer of [the +Less CSS project](http://lesscss.org/), and he introduced himself to use by +overhauling JSHint's variable tracking logic. + +JSHint 3.0 is closer than ever. We're excited for the opportunity to break a +few things in order to make real strides forward. In fact, the hardest part +will be *limiting* ourselves (we don't want to make migrating to the new +version onerous). If you have any ideas along these lines, please submit them +on [the project's issue tracker](https://github.com/jshint/jshint/issues). +We'll mark them with [the label "Breaking +Change"](https://github.com/jshint/jshint/labels/Breaking%20Change), and as we +decide what's definitely "in" for 3.0, we'll add them to [the "v3.0.0" +milestone](https://github.com/jshint/jshint/milestones/v3.0.0). + +### Bug Fixes + +* Add `HTMLCollection` to browser environment. ([e92d375](https://github.com/jshint/jshint/commit/e92d375)), closes [#2443](https://github.com/jshint/jshint/issues/2443) +* Add `window.performance` to browser vars ([3ff1b05](https://github.com/jshint/jshint/commit/3ff1b05)), closes [#2461](https://github.com/jshint/jshint/issues/2461) +* Allow `__proto__` when using ES6 ([06b5764](https://github.com/jshint/jshint/commit/06b5764)), closes [#2371](https://github.com/jshint/jshint/issues/2371) +* Allow binary and octal numbers, and templates when using inline `esnext` ([b5ba7d6](https://github.com/jshint/jshint/commit/b5ba7d6)), closes [#2519](https://github.com/jshint/jshint/issues/2519) +* Allow default values in destructuring. ([04ace9a](https://github.com/jshint/jshint/commit/04ace9a)), closes [#1941](https://github.com/jshint/jshint/issues/1941) +* Allow destructuring in catch blocks parameter ([759644c](https://github.com/jshint/jshint/commit/759644c)), closes [#2526](https://github.com/jshint/jshint/issues/2526) +* Allow latedef in the initialiser of variable ([18f8775](https://github.com/jshint/jshint/commit/18f8775)), closes [#2628](https://github.com/jshint/jshint/issues/2628) +* Allow line breaking after yield if `asi: true` ([728c84b](https://github.com/jshint/jshint/commit/728c84b)), closes [#2530](https://github.com/jshint/jshint/issues/2530) +* Allow non-identifier PropertyNames in object destructuring. ([aa8a023](https://github.com/jshint/jshint/commit/aa8a023)), closes [#2467](https://github.com/jshint/jshint/issues/2467) +* Allow object destructuring assignment ([ae48966](https://github.com/jshint/jshint/commit/ae48966)), closes [#2269](https://github.com/jshint/jshint/issues/2269) +* Allow semicolon as string value in JSON ([ab73e01](https://github.com/jshint/jshint/commit/ab73e01)) +* block scope vars dont redefine in blocks ([9e74025](https://github.com/jshint/jshint/commit/9e74025)), closes [#2438](https://github.com/jshint/jshint/issues/2438) +* Catch blocks are no longer functions ([8a864f3](https://github.com/jshint/jshint/commit/8a864f3)), closes [#2510](https://github.com/jshint/jshint/issues/2510) +* Change imported variables to be constants ([94a6779](https://github.com/jshint/jshint/commit/94a6779)), closes [#2428](https://github.com/jshint/jshint/issues/2428) +* Classes are not hoisted ([87378cc](https://github.com/jshint/jshint/commit/87378cc)), closes [#1934](https://github.com/jshint/jshint/issues/1934) +* Correct exported AssignmentExpressions ([282b40e](https://github.com/jshint/jshint/commit/282b40e)) +* Correctly parse empty destructuring ([97c188b](https://github.com/jshint/jshint/commit/97c188b)), closes [#2513](https://github.com/jshint/jshint/issues/2513) +* Correctly parse exported generators ([0604816](https://github.com/jshint/jshint/commit/0604816)), closes [#2472](https://github.com/jshint/jshint/issues/2472) +* Declare `func` as a property of `state` ([3be8d36](https://github.com/jshint/jshint/commit/3be8d36)) +* default params can't reference future arg ([bc2741c](https://github.com/jshint/jshint/commit/bc2741c)), closes [#2422](https://github.com/jshint/jshint/issues/2422) +* Define "build" module ([2f98f91](https://github.com/jshint/jshint/commit/2f98f91)) +* Define npm scripts for each test suite ([5c33ded](https://github.com/jshint/jshint/commit/5c33ded)) +* Do not accept empty values for directives ([a5bfefb](https://github.com/jshint/jshint/commit/a5bfefb)) +* Do not crash if the forin check is block ([d1cbe84](https://github.com/jshint/jshint/commit/d1cbe84)), closes [#1920](https://github.com/jshint/jshint/issues/1920) +* Do not mark `ignore` directives as special ([f14c262](https://github.com/jshint/jshint/commit/f14c262)) +* Do not parse arrays which contain `for` as array comprehensions. ([d70876c](https://github.com/jshint/jshint/commit/d70876c)), closes [#1413](https://github.com/jshint/jshint/issues/1413) +* Don't crash on uncomplete typeof expression ([a32cf50](https://github.com/jshint/jshint/commit/a32cf50)), closes [#2506](https://github.com/jshint/jshint/issues/2506) +* Don't warn when Array() is used without 'new'. ([5f88aa7](https://github.com/jshint/jshint/commit/5f88aa7)), closes [#1987](https://github.com/jshint/jshint/issues/1987) +* Dont crash when testing x == keyword if eqnull is on ([6afd373](https://github.com/jshint/jshint/commit/6afd373)), closes [#2587](https://github.com/jshint/jshint/issues/2587) +* Dont warn twice in var redeclaration ([e32e17b](https://github.com/jshint/jshint/commit/e32e17b)) +* handle no 'home' environment variables ([946af3e](https://github.com/jshint/jshint/commit/946af3e)) +* Honor `ignore` directive more consistently ([0971608](https://github.com/jshint/jshint/commit/0971608)) +* Ignore directive should ignore max line length for comments ([f2f871a](https://github.com/jshint/jshint/commit/f2f871a)), closes [#1575](https://github.com/jshint/jshint/issues/1575) +* Immediately-invoked arrow funcs' param doesn't need parentheses ([d261071](https://github.com/jshint/jshint/commit/d261071)), closes [#2351](https://github.com/jshint/jshint/issues/2351) +* Improve support for `__proto__` identifier ([925a983](https://github.com/jshint/jshint/commit/925a983)) +* It is not un-necessary to assign undefined in a loop ([e8ce9bf](https://github.com/jshint/jshint/commit/e8ce9bf)), closes [#1191](https://github.com/jshint/jshint/issues/1191) +* labeled break and continue semantics ([da66f70](https://github.com/jshint/jshint/commit/da66f70)) +* Labels shadowing within a function is a syntax error ([124e00f](https://github.com/jshint/jshint/commit/124e00f)), closes [#2419](https://github.com/jshint/jshint/issues/2419) +* Load JSHint from package root ([92acdd1](https://github.com/jshint/jshint/commit/92acdd1)) +* Make `strict: func` have precedence over env options. ([d138db8](https://github.com/jshint/jshint/commit/d138db8)) +* Param destructuring should not effect max params ([9d021ee](https://github.com/jshint/jshint/commit/9d021ee)), closes [#2183](https://github.com/jshint/jshint/issues/2183) +* Params cannot always have the same name ([9f2b64c](https://github.com/jshint/jshint/commit/9f2b64c)), closes [#2492](https://github.com/jshint/jshint/issues/2492) +* Prevent regressions in bug fix ([477d3ad](https://github.com/jshint/jshint/commit/477d3ad)) +* Regular args can come after args with default ([f2a59f1](https://github.com/jshint/jshint/commit/f2a59f1)), closes [#1779](https://github.com/jshint/jshint/issues/1779) +* Relax restriction on `module` option ([56c19a5](https://github.com/jshint/jshint/commit/56c19a5)) +* Remove bad error E048 in for loop init ([a8fc16b](https://github.com/jshint/jshint/commit/a8fc16b)), closes [#1862](https://github.com/jshint/jshint/issues/1862) +* Remove module import declaration ([1749ac0](https://github.com/jshint/jshint/commit/1749ac0)) +* Report an error when a necessary semicolon is missing ([45d8e3e](https://github.com/jshint/jshint/commit/45d8e3e)), closes [#1327](https://github.com/jshint/jshint/issues/1327) +* report line numbers of destructured params ([7d25451](https://github.com/jshint/jshint/commit/7d25451)), closes [#2494](https://github.com/jshint/jshint/issues/2494) +* Report loopfunc for all function types ([4d4cfcd](https://github.com/jshint/jshint/commit/4d4cfcd)), closes [#2153](https://github.com/jshint/jshint/issues/2153) +* singleGroups: Allow grouping for integers ([8c265ca](https://github.com/jshint/jshint/commit/8c265ca)) +* Support `new.target` ([2fbf621](https://github.com/jshint/jshint/commit/2fbf621)) +* The `__iterator__` property is deprecated. ([7780613](https://github.com/jshint/jshint/commit/7780613)) +* Unify assign operation checks. ([06eb1d2](https://github.com/jshint/jshint/commit/06eb1d2)), closes [#2589](https://github.com/jshint/jshint/issues/2589) +* use of params is not capturing loopfunc ([827e335](https://github.com/jshint/jshint/commit/827e335)) +* Warn about using `var` inside `for (...)` when `varstmt: true` ([f1ab638](https://github.com/jshint/jshint/commit/f1ab638)), closes [#2627](https://github.com/jshint/jshint/issues/2627) + +### Features + +* Add `esversion` option ([cf5a699](https://github.com/jshint/jshint/commit/cf5a699)), closes [#2124](https://github.com/jshint/jshint/issues/2124) +* Add pending to Jasmine's globals ([02790b9](https://github.com/jshint/jshint/commit/02790b9)), closes [#2154](https://github.com/jshint/jshint/issues/2154) +* Add Window constructor to browser vars ([7f5806f](https://github.com/jshint/jshint/commit/7f5806f)), closes [#2132](https://github.com/jshint/jshint/issues/2132) +* Option to assume strict mode ([8de8247](https://github.com/jshint/jshint/commit/8de8247)), closes [#924](https://github.com/jshint/jshint/issues/924) +* Support multiple files in the exclude option ([bd4ec25](https://github.com/jshint/jshint/commit/bd4ec25)) + + + + +# [2.8.0](https://github.com/jshint/jshint/compare/2.7.0...2.8.0) (2015-05-31) + + +### Bug Fixes + +* add the "fetch" global for "browser" environment ([b3b41c8](https://github.com/jshint/jshint/commit/b3b41c8)), closes [#2355](https://github.com/jshint/jshint/issues/2355) +* Allow lexer to communicate completion ([a093f78](https://github.com/jshint/jshint/commit/a093f78)) +* Distinguish between directive and mode ([51059bd](https://github.com/jshint/jshint/commit/51059bd)) +* Don't throw "Duplicate class method" with computed method names ([ab12dfb](https://github.com/jshint/jshint/commit/ab12dfb)), closes [#2350](https://github.com/jshint/jshint/issues/2350) +* Ignore unused arrow-function parameters if unused: vars ([2ea9cb0](https://github.com/jshint/jshint/commit/2ea9cb0)), closes [#2345](https://github.com/jshint/jshint/issues/2345) +* Move helper methods to `state` object ([678da76](https://github.com/jshint/jshint/commit/678da76)) +* parse `const` declarations in ForIn/Of loops ([2b673d9](https://github.com/jshint/jshint/commit/2b673d9)), closes [#2334](https://github.com/jshint/jshint/issues/2334) [#2335](https://github.com/jshint/jshint/issues/2335) +* Parse semicolons in class bodies ([58c8e64](https://github.com/jshint/jshint/commit/58c8e64)) +* Prevent regression in `enforceall` ([6afcde4](https://github.com/jshint/jshint/commit/6afcde4)) +* Relax singleGroups restrictions: arrow fns ([4a4f522](https://github.com/jshint/jshint/commit/4a4f522)) +* Relax singleGroups restrictions: IIFEs ([9f55160](https://github.com/jshint/jshint/commit/9f55160)) +* Reset generator flag for each method definition ([2444a04](https://github.com/jshint/jshint/commit/2444a04)), closes [#2388](https://github.com/jshint/jshint/issues/2388) [#2389](https://github.com/jshint/jshint/issues/2389) + +### Features + +* Implement `module` option ([290280c](https://github.com/jshint/jshint/commit/290280c)) +* support destructuring in ForIn/Of loops, lint bad ForIn/Of LHS ([c0edd9f](https://github.com/jshint/jshint/commit/c0edd9f)), closes [#2341](https://github.com/jshint/jshint/issues/2341) + + + + +# [2.7.0](https://github.com/jshint/jshint/compare/2.6.3...2.7.0) (2015-04-10) + + +### Bug Fixes + +* Accept `get` and `set` as ID properties ([2ad235c](https://github.com/jshint/jshint/commit/2ad235c)) +* allow trailing comma in ArrayBindingPattern ([3477933](https://github.com/jshint/jshint/commit/3477933)), closes [#2222](https://github.com/jshint/jshint/issues/2222) +* allow typeof symbol === "symbol" ([7f7aac2](https://github.com/jshint/jshint/commit/7f7aac2)), closes [#2241](https://github.com/jshint/jshint/issues/2241) [#2242](https://github.com/jshint/jshint/issues/2242) +* Correctly enforce maxparams:0 ([011364e](https://github.com/jshint/jshint/commit/011364e)) +* default to empty string in src/cli.js loadIgnores ([0eeba14](https://github.com/jshint/jshint/commit/0eeba14)) +* disallow 'lone' rest operator in identifier() ([dd08f85](https://github.com/jshint/jshint/commit/dd08f85)), closes [#2222](https://github.com/jshint/jshint/issues/2222) +* emit I003 more carefully and less annoyingly ([757fb73](https://github.com/jshint/jshint/commit/757fb73)), closes [#2251](https://github.com/jshint/jshint/issues/2251) +* export all names for var/let/const declarations ([3ce1267](https://github.com/jshint/jshint/commit/3ce1267)), closes [#2248](https://github.com/jshint/jshint/issues/2248) [#2253](https://github.com/jshint/jshint/issues/2253) [#2252](https://github.com/jshint/jshint/issues/2252) +* Incorrect 'Unclosed string' when the closing quote is the first character after a newline ([b804e65](https://github.com/jshint/jshint/commit/b804e65)), closes [#1532](https://github.com/jshint/jshint/issues/1532) [#1532](https://github.com/jshint/jshint/issues/1532) [#1319](https://github.com/jshint/jshint/issues/1319) +* predefine HTMLTemplateElement in browser ([231557a](https://github.com/jshint/jshint/commit/231557a)), closes [#2246](https://github.com/jshint/jshint/issues/2246) +* Prevent incorrect warnings for relations ([64f85f3](https://github.com/jshint/jshint/commit/64f85f3)) +* Relax restrictions on `singleGroups` ([896bf82](https://github.com/jshint/jshint/commit/896bf82)) +* templates are operands, not operators ([162dee6](https://github.com/jshint/jshint/commit/162dee6)), closes [#2223](https://github.com/jshint/jshint/issues/2223) [#2224](https://github.com/jshint/jshint/issues/2224) + +### Features + +* add `varstmt` enforcement option to disallow use of VariableStatements ([59396f7](https://github.com/jshint/jshint/commit/59396f7)), closes [#1549](https://github.com/jshint/jshint/issues/1549) + + + + +## [2.6.3](https://github.com/jshint/jshint/compare/2.6.2...2.6.3) (2015-02-28) + + +### Bug Fixes + +* parse trailing comma in ObjectBindingPattern ([7a2b713](https://github.com/jshint/jshint/commit/7a2b713)), closes [#2220](https://github.com/jshint/jshint/issues/2220) + + + + +## [2.6.2](https://github.com/jshint/jshint/compare/2.6.1...2.6.2) (2015-02-28) + + +### Bug Fixes + +* Disable `futurehostile` option by default ([3cbd41f](https://github.com/jshint/jshint/commit/3cbd41f)) +* Make let variables in the closure shadow predefs ([cfd2e0b](https://github.com/jshint/jshint/commit/cfd2e0b)) + + + + +## [2.6.1](https://github.com/jshint/jshint/compare/2.6.0...2.6.1) (2015-02-27) + + +### Bug Fixes + +* Allow object-literals within template strings ([4f08b74](https://github.com/jshint/jshint/commit/4f08b74)), closes [#2082](https://github.com/jshint/jshint/issues/2082) +* Correct behavior of `singleGroups` ([6003c83](https://github.com/jshint/jshint/commit/6003c83)) +* Correct token reported by `singleGroups` ([bc857f3](https://github.com/jshint/jshint/commit/bc857f3)) +* Disambiguate argument ([d75ef69](https://github.com/jshint/jshint/commit/d75ef69)) +* Do not crash on improper use of `delete` ([35df49f](https://github.com/jshint/jshint/commit/35df49f)) +* ES6 modules respect undef and unused ([438d928](https://github.com/jshint/jshint/commit/438d928)) +* Fix false positives in 'nocomma' option ([33612f8](https://github.com/jshint/jshint/commit/33612f8)) +* Handle multi-line tokens after return or yield ([5c9c7fd](https://github.com/jshint/jshint/commit/5c9c7fd)), closes [#1814](https://github.com/jshint/jshint/issues/1814) [#2142](https://github.com/jshint/jshint/issues/2142) +* Miss xdescribe/xit/context/xcontext in mocha ([8fe6610](https://github.com/jshint/jshint/commit/8fe6610)) +* Parse nested templates ([3da1eaf](https://github.com/jshint/jshint/commit/3da1eaf)), closes [#2151](https://github.com/jshint/jshint/issues/2151) [#2152](https://github.com/jshint/jshint/issues/2152) +* Permit "eval" as object key ([b5f5d5d](https://github.com/jshint/jshint/commit/b5f5d5d)) +* Prevent beginning array from being confused for JSON ([813d97a](https://github.com/jshint/jshint/commit/813d97a)) +* Refactor `doFunction` ([06b5d40](https://github.com/jshint/jshint/commit/06b5d40)) +* Remove quotmark linting for NoSubstTemplates ([7e80490](https://github.com/jshint/jshint/commit/7e80490)) +* Remove tautological condition ([f0bff58](https://github.com/jshint/jshint/commit/f0bff58)) +* remove unused var ([e69acfe](https://github.com/jshint/jshint/commit/e69acfe)), closes [#2156](https://github.com/jshint/jshint/issues/2156) +* Simulate class scope for class expr names ([ac98a24](https://github.com/jshint/jshint/commit/ac98a24)) +* Support more cases of ES6 module usage ([776ed69](https://github.com/jshint/jshint/commit/776ed69)), closes [#2118](https://github.com/jshint/jshint/issues/2118) [#2143](https://github.com/jshint/jshint/issues/2143) +* Templates can not be directives ([20ff670](https://github.com/jshint/jshint/commit/20ff670)) +* Unfollowable path in lexer. ([065961a](https://github.com/jshint/jshint/commit/065961a)) + +### Features + +* Implement new option `futurehostile` ([da52aa0](https://github.com/jshint/jshint/commit/da52aa0)) +* parse and lint tagged template literals ([4816dbd](https://github.com/jshint/jshint/commit/4816dbd)), closes [#2000](https://github.com/jshint/jshint/issues/2000) + + + + +# [2.6.0](https://github.com/jshint/jshint/compare/2.5.11...2.6.0) (2015-01-21) + + +### Bug Fixes + +* Add missing globals to browser environment ([32f02e0](https://github.com/jshint/jshint/commit/32f02e0)) +* Allow array, grouping and string form to follow spread operator in function call args. ([437655a](https://github.com/jshint/jshint/commit/437655a)), closes [#2060](https://github.com/jshint/jshint/issues/2060) [#2060](https://github.com/jshint/jshint/issues/2060) +* Correct bug in enforcement of `singleGroups` ([5fedda6](https://github.com/jshint/jshint/commit/5fedda6)), closes [#2064](https://github.com/jshint/jshint/issues/2064) +* Remove dead code ([3b5d94a](https://github.com/jshint/jshint/commit/3b5d94a)), closes [#883](https://github.com/jshint/jshint/issues/883) +* Remove dead code for parameter parsing ([a1d5817](https://github.com/jshint/jshint/commit/a1d5817)) +* Revert unnecessary commit ([a70bbda](https://github.com/jshint/jshint/commit/a70bbda)) + +### Features + +* `elision` option to relax "Extra comma" warnings ([cbfc827](https://github.com/jshint/jshint/commit/cbfc827)), closes [#2062](https://github.com/jshint/jshint/issues/2062) +* Add new Jasmine 2.1 globals to "jasmine" option ([343c45e](https://github.com/jshint/jshint/commit/343c45e)), closes [#2023](https://github.com/jshint/jshint/issues/2023) +* Support generators in class body ([ee348c3](https://github.com/jshint/jshint/commit/ee348c3)) + + +### BREAKING CHANGES + +* In projects which do not enable ES3 mode, it is now an error by default to use elision array elements, +also known as empty array elements (such as `[1, , 3, , 5]`) + + + + +## [2.5.11](https://github.com/jshint/jshint/compare/2.5.10...2.5.11) (2014-12-18) + + + + + +## [2.5.10](https://github.com/jshint/jshint/compare/2.5.9...2.5.10) (2014-11-06) + + + + + +## [2.5.9](https://github.com/jshint/jshint/compare/2.5.8...2.5.9) (2014-11-06) + + + + + +## [2.5.8](https://github.com/jshint/jshint/compare/2.5.7...2.5.8) (2014-10-29) + + + + + +## [2.5.7](https://github.com/jshint/jshint/compare/2.5.6...2.5.7) (2014-10-28) + + + + + +## [2.5.6](https://github.com/jshint/jshint/compare/2.5.5...2.5.6) (2014-09-21) + + + + + +## [2.5.5](https://github.com/jshint/jshint/compare/2.5.4...2.5.5) (2014-08-24) + + + + + +## [2.5.4](https://github.com/jshint/jshint/compare/2.5.3...2.5.4) (2014-08-18) + + + + + +## [2.5.3](https://github.com/jshint/jshint/compare/2.5.2...2.5.3) (2014-08-08) + + + + + +## [2.5.2](https://github.com/jshint/jshint/compare/2.5.1...2.5.2) (2014-07-05) + + + + + +## [2.5.1](https://github.com/jshint/jshint/compare/2.5.0...2.5.1) (2014-05-16) + + + + + +# [2.5.0](https://github.com/jshint/jshint/compare/2.4.4...2.5.0) (2014-04-02) + + + + + +## [2.4.4](https://github.com/jshint/jshint/compare/2.4.3...2.4.4) (2014-02-21) + + + + + +## [2.4.3](https://github.com/jshint/jshint/compare/2.4.2...2.4.3) (2014-01-26) + + + + + +## [2.4.2](https://github.com/jshint/jshint/compare/2.4.1...2.4.2) (2014-01-21) + + + + + +## [2.4.1](https://github.com/jshint/jshint/compare/2.4.0...2.4.1) (2014-01-03) + + + + + +# [2.4.0](https://github.com/jshint/jshint/compare/2.3.0...2.4.0) (2013-12-25) + + + + + +# [2.3.0](https://github.com/jshint/jshint/compare/2.2.0...2.3.0) (2013-10-21) + + + + + +# [2.2.0](https://github.com/jshint/jshint/compare/2.1.11...2.2.0) (2013-10-18) + + + + + +## [2.1.11](https://github.com/jshint/jshint/compare/2.1.10...2.1.11) (2013-09-20) + + + + + +## [2.1.10](https://github.com/jshint/jshint/compare/2.1.9...2.1.10) (2013-08-15) + +Thanks to [Dave Camp](https://twitter.com/campd) JSHint now supports list +comprehensions, a declarative way of transforming a list: + + [ for (i of [ 1, 2, 3 ]) i + 2 ]; // Returns [ 3, 4, 5 ] + +Note: SpiderMonkey currently implements a slightly different syntax for list +comprehensions which is also supported by JSHint. + +### Patch summary + +* [ae96e5c](https://github.com/jshint/jshint/commit/ae96e5c1e0fb6a80921c8e9bd1eba8f3c96eaaee) + Fixed [#1220](https://github.com/jshint/jshint/issues/1220/): Add typed array + option, implied by 'node' option +* [27bd241](https://github.com/jshint/jshint/commit/27bd241c17e3bedb4e4b66f44e2612e6d4ef0041) + Fixed [#1222](https://github.com/jshint/jshint/issues/1222/): Update + PhantomJS globals to 1.7 API +* [6c5a085](https://github.com/jshint/jshint/commit/6c5a08553f1fcb2bbd89220b539aa0568ff99481) + Fixed [#1216](https://github.com/jshint/jshint/issues/1216/): Support for + array comprehensions using for-of (closed #1095) +* [83374ad](https://github.com/jshint/jshint/commit/83374adb3dad7c5bf708a8f6488d023d65232660) + No issue: Remove /stable/ subdirectories +* [1a3c47f](https://github.com/jshint/jshint/commit/1a3c47fb1278159e9db2a13e41f442f92e08a099) + Fixed [#1174](https://github.com/jshint/jshint/issues/1174/): Fixed a false + positive 'destructuring assignment' warning (closed #1177) +* [303c535](https://github.com/jshint/jshint/commit/303c53555d36651285a1decc7faacd94f400b7e8) + Fixed [#1183](https://github.com/jshint/jshint/issues/1183/): Fix an issue + with debugger warning pointing to a wrong line in some cases +* [a0b7181](https://github.com/jshint/jshint/commit/a0b7181578c2f07058bd1ff4f11fc622056005a3) + No issue: Add helper programs to apply and land patches from GitHub +* [9c2b8dd](https://github.com/jshint/jshint/commit/9c2b8dd55bcc131420b6326cc56cc2863d0b268f) + Fixed [#1194](https://github.com/jshint/jshint/issues/1194/): Don't look for + a config when input is /dev/stdin +* [a17ae9e](https://github.com/jshint/jshint/commit/a17ae9ed1e01ba465487b97066fdc2ba65ce109a) + Fixed [#1189](https://github.com/jshint/jshint/issues/1189/): Support spaces + in /*global ... */ +* [dcc1251](https://github.com/jshint/jshint/commit/dcc125147455556c8fbc4d51ed59b8afa7174ff3) + Fixed [#1197](https://github.com/jshint/jshint/issues/1197/): Make Rhino + wrapper to be more consistent with NPM package. +* [96ea1a8](https://github.com/jshint/jshint/commit/96ea1a88f19681f35ca534045aa6e990a39713ca) + No issue: Split make.js into bin/build and bin/changelog +* [4ac19fa](https://github.com/jshint/jshint/commit/4ac19fa53016dfc8686d0ec882da2269aee1e964) + No issue: Move JSHint config into package.json + +**Thanks** to Rob Wu, Ryan Cannon, Dave Camp, Amir Livneh, Josh Hoff, Nikolay +S. Frantsev, Lapo Luchini, Lukas Domnick for sending patches! + + + +## [2.1.9](https://github.com/jshint/jshint/compare/2.1.8...2.1.9) (2013-08-02) + + + + + +## [2.1.8](https://github.com/jshint/jshint/compare/2.1.7...2.1.8) (2013-08-01) + + + + + +## [2.1.7](https://github.com/jshint/jshint/compare/2.1.6...2.1.7) (2013-07-29) + + + + + +## [2.1.6](https://github.com/jshint/jshint/compare/2.1.5...2.1.6) (2013-07-29) + +**UPDATE:** We just published another version, 2.1.7, which contains only one +bugfix: [#1199](https://github.com/jshint/jshint/pull/1199). + +In this release we added two new arguments to our CLI program: `exclude` which +allows you to exclude directories from linting and `prereq` which allows you to +specify a file containing declarations of the global variables used throughout +your project. In addition to that, we added support for stdin. JSHint now +follows a UNIX convention where if a given file path is a dash (`-`) the the +program reads from stdin. + +We also extended our ES6 coverage by adding support for `yield` statements and +`import/export` declarations. JSHint is still **the only** linter that can +parse most ES6 and Mozilla-specific JavaScript code. + +For more changes, see the patch summary below. + +* [004dc61](https://github.com/jshint/jshint/commit/004dc619b263449ab8e05635428f0dabc80ae9b2) + Fixed [#1178](https://github.com/jshint/jshint/issues/1178/): Changed + 'predef' to 'globals' in the example .jshintrc +* [cd69f13](https://github.com/jshint/jshint/commit/cd69f1390bd40d02b6d8dc06abddc4d744310981) + Fixed [#1187](https://github.com/jshint/jshint/issues/1187/): Explicitly + define contents of our NPM package +* [c83caf3](https://github.com/jshint/jshint/commit/c83caf33a3c867e557039433b25bb57a5be6ae5f) + Fixed [#1166](https://github.com/jshint/jshint/issues/1166/): Tweaks to + import/export support +* [537dcbd](https://github.com/jshint/jshint/commit/537dcbd4be49f5b52ede08e98b23e49bbc5e4bbc) + Fixed [#1164](https://github.com/jshint/jshint/issues/1164/): Add codes to + errors generated by quit() +* [6aed7ed](https://github.com/jshint/jshint/commit/6aed7ede44f16dc5195831fa6d85ba9c75b40394) + Fixed [#1155](https://github.com/jshint/jshint/issues/1155/): Use shelljs + option in make.js +* [87df213](https://github.com/jshint/jshint/commit/87df213d19dffc75a30f4929d9302ab2e653e332) + Fixed [#1153](https://github.com/jshint/jshint/issues/1153/): Moved E037 and + E038 to the warnings section and changed their message. +* [dd060c7](https://github.com/jshint/jshint/commit/dd060c7373971cac2a965deee38d72ff5214d417) + Fixed [#779](https://github.com/jshint/jshint/issues/779/): Add support for + !pattern in the .jshintignore files +* [5de09c4](https://github.com/jshint/jshint/commit/5de09c434a62f9a63086959fd8ddb8d8d7369d50) + Fixed [#696](https://github.com/jshint/jshint/issues/696/): Add support for + `--exclude` arg +* [ee3d598](https://github.com/jshint/jshint/commit/ee3d59830b0cea0d7cb814e8ac654f25d9f38f03) + Fixed [#809](https://github.com/jshint/jshint/issues/809/): Added short + options to bin/jshint where it made sense +* [b937895](https://github.com/jshint/jshint/commit/b9378950554277c9b67ad01ab537d2ca94e59294) + Fixed [#810](https://github.com/jshint/jshint/issues/810/): Made --reporter + description in -h more straightforward +* [1c70362](https://github.com/jshint/jshint/commit/1c703625e26f95f1a77263e52dbdbcc494eeed01) + Fixed [#839](https://github.com/jshint/jshint/issues/839/): Add support for + prereq files +* [28dae4b](https://github.com/jshint/jshint/commit/28dae4baf2286d6044e96851b0acf52945262bb4) + Fixed [#741](https://github.com/jshint/jshint/issues/741/): expose loadConfig + from CLI +* [b39e2ac](https://github.com/jshint/jshint/commit/b39e2acea8ad53e9d288e1ec94d829dce26dfd5e) + Followup [#687](https://github.com/jshint/jshint/issues/687/): + eqnull +* [90b733b](https://github.com/jshint/jshint/commit/90b733bcf2c13e196039d994b8d374acbd0b5c28) + Followup [#687](https://github.com/jshint/jshint/issues/687/): Use '-' as a + marker for stding +* [68db0d8](https://github.com/jshint/jshint/commit/68db0d82d11426072a28409a1101ea180fa957eb) + Fixed [#687](https://github.com/jshint/jshint/issues/687/): Allow input via + stdin +* [5924b2a](https://github.com/jshint/jshint/commit/5924b2aa5aafdf8fede525b7156bd1962f824a14) + Fixed [#1157](https://github.com/jshint/jshint/issues/1157/): Add support for + import/export. +* [729cfd7](https://github.com/jshint/jshint/commit/729cfd718cf11585bd03713d314d1367d92ac7d7) + Fixed [#1154](https://github.com/jshint/jshint/issues/1154/): Add MouseEvent + and CustomEvent browser globals +* [9782fc8](https://github.com/jshint/jshint/commit/9782fc812703e60cfee8acae347aab4dd065844b) + Fixed [#1134](https://github.com/jshint/jshint/issues/1134/): Catch reserved + words in ES3 mode. +* [87e3e6c](https://github.com/jshint/jshint/commit/87e3e6ccfb3c37417a56946dce5904742bd43311) + Fixed [#1138](https://github.com/jshint/jshint/issues/1138/): Count ternary + and or operators for complexity +* [66f3e4c](https://github.com/jshint/jshint/commit/66f3e4c13434de9c9951dfff084b438db9ed525f) + Fixed [#1133](https://github.com/jshint/jshint/issues/1133/): Make shelljs + imply node. +* [79dc812](https://github.com/jshint/jshint/commit/79dc812bfd7510e196d811653db406d2001e159f) + Fixed [#704](https://github.com/jshint/jshint/issues/704/): Add config file + support for the Rhino wrappers. +* [88c862d](https://github.com/jshint/jshint/commit/88c862df3dba9e2cfa1e44d4be909099d8306c97) + Fixed [#1109](https://github.com/jshint/jshint/issues/1109/): Parse yield + expressions. + +**Thanks** to Terry Roe, Sindre Sorhus, Thomas Boyt, Nikolay S. Frantsev, +XhmikosR, Jacob Rask, Kevin Chu, Tim Ruffles, Stephen Mathieson, Lukas Domnick, +usrbincc for sending patches! + + +## [2.1.5](https://github.com/jshint/jshint/compare/2.1.4...2.1.5) (2013-07-27) + + + + + +## [2.1.4](https://github.com/jshint/jshint/compare/2.1.3...2.1.4) (2013-06-24) + + + + + +## [2.1.3](https://github.com/jshint/jshint/compare/2.1.2...2.1.3) (2013-06-03) + + + + + +## [2.1.2](https://github.com/jshint/jshint/compare/2.1.1...2.1.2) (2013-05-22) + + + + + +## [2.1.1](https://github.com/jshint/jshint/compare/2.1.0...2.1.1) (2013-05-21) + + + + + +# [2.1.0](https://github.com/jshint/jshint/compare/2.0.1...2.1.0) (2013-05-21) + +JSHint 2.1.0 is out. This releases adds support for ES6 `class` syntax and +fixes some issues with our parser. + +* Added support for ES6 `class` syntax. + ([#1048](https://github.com/jshint/jshint/pull/1048)) +* Added support for error code in the Checkstyle reporter. + ([#1088](https://github.com/jshint/jshint/pull/1088)) +* Added support for `do` statement bodies that are not block + statements. + ([#1062](https://github.com/jshint/jshint/pull/1062)) +* Fixed issues with JSHint not parsing comma expressions correctly. + ([#1084](https://github.com/jshint/jshint/pull/1084)) +* Fixed a bug with W080 no longer pointing to relevant identifiers. + ([#1070](https://github.com/jshint/jshint/pull/1070)) +* Fixed a potential issue with Node 0.10 and Windows. + ([#1065](https://github.com/jshint/jshint/pull/1065)) +* Fixed issues with JSHint not parsing assignments in `switch` + conditionals. + ([#1064](https://github.com/jshint/jshint/pull/1064)) +* Fixed an issue with `esnext` and `moz` modes turning off the + default `es5` mode. + ([#1068](https://github.com/jshint/jshint/issues/1068)) + +**Thanks** to usrbincc, James Allardice, Iraê Carvalho, Nick Schonning and +jklein for sending patches! + + +## [2.0.1](https://github.com/jshint/jshint/compare/2.0.0...2.0.1) (2013-05-08) + + + + + +# [2.0.0](https://github.com/jshint/jshint/compare/1.1.0...2.0.0) (2013-05-08) + +**WARNING:** This release introduces backwards incompatible changes. + +JSHint 2.0.0 is out! This version hits a pretty big milestone for the project: +this is the first JSHint release for which I'm not the biggest contributor. I +personally believe this fact validates JSHint as a successful *open source* +project. And I'm extremely thankful to all you who file bug reports and send +patches—you're all awesome. + +#### EcmaScript 5 + +The first and foremost: starting with this version JSHint will assume ES5 as +the default environment. Before, JSHint was checking all the code per ES3 +specification with an option to enable ES5 mode. Now ES5 mode is the default +mode and if you want to check your code against the ES3 specification (useful +when developing for super old browsers such as Internet Explorer 6) you will +have to use `es3:true`. + +Special thanks to Rick Waldron for championing this change. + +#### Partial support for Mozilla JavaScript extensions and ES6 + +Thanks to our newest core contributor, Bernard Pratz, JSHint now has partial +support for Mozilla JavaScript extensions (`moz` option) and ES6 (`esnext` +option): + +* Destructuring assignment +* `const` +* `let` blocks and expressions +* Generators and iterators +* List comprehension +* Try/catch filters and multiple catch blocks +* Concise method declaration +* `for ... of` loops +* Fat arrows + +We have more patches in queue that add support for classes and other nifty ES6 +things. Stay tuned! + +#### CLI + +* JSHint now looks for `.jshintrc` in the directory being linted. + ([#833](https://github.com/jshint/jshint/issues/833)) +* Various cross-platform fixes for our Node CLI module. +* New public method for the CLI export that allows third-parties to hook into + the file resolution logic. + ([#741](https://github.com/jshint/jshint/issues/741)) + +#### General + +* For non-Node system we upgraded to the latest version of Browserify. This + resolves some performance issues we had with Rhino. +* Added SVG globals to the browser environment. +* Option `smarttabs` now ignores mixed tabs and spaces within single- + and multi-line comments. +* Added a new pragma to unignore a warning: + + /*jshint -W096 */ + + // All warnings about keys producing unexpected results will + // be ignored here. + + /*jshint +W096 */ + + // But not here. + +* JSHint now ignores unrecognized JSLint options. +* Fixed a bug where `indent:false` was triggering indentation warnings. + ([#1035](https://github.com/jshint/jshint/issues/1035)) +* Fixed a regression bug where `unused` was not behaving correctly. + ([#996](https://github.com/jshint/jshint/issues/996)) +* Plus lots and lots of other, smaller bug fixes. + +#### New rapid release schedule + +And last but not least: starting with this version, I'm switching JSHint to a +more rapid release schedule. This simply means that I will be publishing new +versions of JSHint more often. I will try my best to follow +[semver](http://semver.org/) recommendations and ship working software. But as +our license says, no guarantees. + +**Thanks** to Bernarnd Pratz, Michelle Steigerwalt, Yuya Tanaka, Matthew +Flaschen, Juan Pablo Buritica, Matt Cheely, Steve Mosley, Stephen Sorensen, +Rick Waldron, Hugues Malphettes, Jeff Thompson, xzyfer, Lee Leathers, croensch, +Steven Benner, James Allardice, Sindre Sorhus, Jordan Harband, Stuart Knightley +and Kevin Locke for sending patches! + + +# [1.1.0](https://github.com/jshint/jshint/compare/1.0.0...1.1.0) (2013-03-06) + + + + + +# [1.0.0](https://github.com/jshint/jshint/compare/1.0.0-rc4...1.0.0) (2013-01-30) + + + + + +# [1.0.0-rc4](https://github.com/jshint/jshint/compare/1.0.0-rc3...1.0.0-rc4) (2013-01-18) + +JSHint 1.0.0 Release Candidate 4 is now out: + +* Fixes a bug with JSHint not allowing reserved words to be used as property + names. ([#768](https://github.com/jshint/jshint/issues/768)) +* Fixes a bug with JSHint lexer not recognizing `/` after `]`. + ([#803](https://github.com/jshint/jshint/issues/803)) +* Fixes a bug with JSHint not recognizing `predef` when its value is an array, + and not an object. ([#800](https://github.com/jshint/jshint/issues/800)) +* Fixes a bug with JSHint crashing on unrecoverable syntax errors such as + `if (name <) {}`. ([#818](https://github.com/jshint/jshint/issues/818)) + +Here's how you can install this release candidate: + + $ npm install https://github.com/jshint/jshint/archive/1.0.0-rc4.tar.gz + +For full 1.0.0 changelog please see our +[1.0.0 RC1 announcement](http://jshint.com/blog/2012-12-29/1-0-0-rc1/). + + +# [1.0.0-rc3](https://github.com/jshint/jshint/compare/1.0.0-rc2...1.0.0-rc3) (2013-01-02) + +JSHint 1.0.0 Release Candidate 3 is now out: + +* Fixes a bug with JSHint not allowing `new` and `debugger` to + appear after a comma. ([#793](https://github.com/jshint/jshint/issues/793)) +* Fixes a bug with JSHint not collecting file recursively. + ([#794](https://github.com/jshint/jshint/issues/794)) +* Fixes a bug with JSHint crashing when future reserved words are used as + identifiers. +* Adds a newline separator between files in the CLI output. +* Fixes a bug with JSHint not parsing `/*global global:true */` correctly. + ([#795](https://github.com/jshint/jshint/issues/795)) +* Fixes a bug with JSHint crashing when files can't be found. + +Here's how you can install this release candidate: + + $ npm install https://github.com/jshint/jshint/archive/1.0.0-rc3.tar.gz + +For full 1.0.0 changelog please see our [1.0.0 RC1 +announcement](http://jshint.com/blog/2012-12-29/1-0-0-rc1/). + + +# [1.0.0-rc2](https://github.com/jshint/jshint/compare/1.0.0-rc1...1.0.0-rc2) (2012-12-31) + +JSHint 1.0.0 Release Candidate 2 is now out: + +* Fixes a bug with JSHint not recognizing regular expressions after commas. + ([#792](https://github.com/jshint/jshint/pull/792)) +* Fixes two failed tests on Windows. + ([#790](https://github.com/jshint/jshint/pull/790)) +* Fixes a bug with JSHint builder failing when there is no dist/ directory. + ([#788](https://github.com/jshint/jshint/pull/788)) +* Adds JSHint binary to *package.json* so that JSHint could be, once again, + installed and used globally as a CLI program. + ([#787](https://github.com/jshint/jshint/pull/787)) + +Here's how you can install this release candidate: + + $ npm install https://github.com/jshint/jshint/archive/1.0.0-rc2.tar.gz + +For full 1.0.0 changelog please see our +[1.0.0 RC1 announcement](http://jshint.com/blog/2012-12-29/1-0-0-rc1/). + +Big thanks to Samuel Cole for submitting patches and finding bugs! + + +# 1.0.0-rc1 (2012-12-31) + +After three months and 100+ commits, JSHint 1.0.0 is ready for release. This +is the biggest release for JSHint so far, and that's why I've decided to run it +through a release candidate phase first. I tried my best to make it as +backwards compatible as possible, but there might be a small number of +incompatibilities depending on how you use JSHint. Please keep that in mind and +test your integration before updating to 1.0.0. + +One of the biggest changes is that node-jshint is now part of the main JSHint +project, which means that there will no longer be lag time between releasing a +new version and publishing it on NPM. **Node and NPM is now the main and +recommended way of using JSHint on all platforms.** This also means that +starting with "1.0.0", JSHint will start using the +[node-semver](https://github.com/isaacs/node-semver) versioning system instead +of the old rN system. + +In addition, this version drops support for non-ES5 environments. This means +that JavaScript engines that don't support the ES5 syntax will not even parse +JSHint's source code. (For example, the online interface for JSHint will not +work in older versions of IE.) + +I'm very excited to finally release this version and I encourage everyone to +try out the release candidate and report any bugs and issues you encounter. The +full changelog is provided below, with examples and links to relevant issues. + +#### Parser + +This version has a completely rewritten lexer. Since it's no longer a giant +regexp, the new lexer is more robust and easier to read. I'd like to thank the +authors of Esprima and Traceur since I borrowed some pieces from them. + +* This version **adds support for Unicode identifiers!** + ([#301](https://github.com/jshint/jshint/issues/301) and + [#716](https://github.com/jshint/jshint/issues/716/)) + + var π = 3.1415; + +* Adds support for the comma operator. ([#56](https://github.com/jshint/jshint/issues/56/)) + JSHint now parses code like the following (note the comma in the middle + expression): + + for (var i = 0, ch; ch = channels[i], i < channels.length; i++) { + // ... + } + +* Improves support for numbers. JSHint now understands numbers with leading + dots (e.g. .12) and doesn't generate false positives on invalid numbers (e.g. + 099). In case of invalid numbers the parser still parses them but marks as + malformed and generates a nice little warning. + +* Adds support for more relaxed JSHint directive syntax. JSHint now recognizes + space between `/*` and jshint/global/etc. and allows you to use single-line + comments for directives in addition to multi-line comments: + + Before: + /*jshint strict:true */ + + Now: + /*jshint strict:true */ + /* jshint strict:true */ (note the space) + //jshint strict:true + // jshint strict:true + + One potentially breaking change is that all lists inside JSHint directives + must be separated by commas from now on. So `/*jshint strict:true undef:true + */` won't fly anymore but `/*jshint strict:true, undef:true */` will (note + the comma). + +* Adds better parser for regular expressions. Previously, JSHint would check + the grammar of regular expressions using its own internal logic. Now, JSHint + compiles the parsed expressions using the native RegExp object to check + for grammar errors. + +* Adds support for a defensive semicolon before `[`. (Ticket + [#487](https://github.com/jshint/jshint/issues/487/)) + +* Adds support for unclosed multi-line strings and removes warnings about + unnecessary escaping for `\u` and `\x` in strings. + ([#494](https://github.com/jshint/jshint/issues/494/)) + +Bug fixes: + +* Fixes a bug with JSHint not warning about reserved words being used as + variable and function declaration identifiers. (Ticket + [#744](https://github.com/jshint/jshint/issues/744/)) + +* Fixes a bug with JSHint generating a false positive *Missing whitespace...* + warning on trailing commas. + ([#363](https://github.com/jshint/jshint/issues/363/)) + +* Fixes a bug with JSHint not being able to parse regular expressions preceded + by *typeof* (e.g. `typeof /[a-z]/`) and, in some cases, \*=, /=, etc. (e.g. + `if (x /= 2) { ... }`). + ([#657](https://github.com/jshint/jshint/issues/657/)) + +#### General + +* This version adds a unique numeric code to every warning and error message + produced by JSHint. That means that you can now **ignore any warning** + produced by JSHint even when there is no corresponding option for it. You can + do that using the special minus (-) operator. For example, here's how you + ignore all messages about trailing decimal points (W047): + + /*jshint -W047 */ + + or + + JSHINT(src, { "-W047": true }); + + Keep in mind that this syntax can't be used to ignore errors. + +* Due to popular demand, this version splits *indent* and *white* options + meaning that *indent* won't imply *white* anymore. + ([#667](https://github.com/jshint/jshint/issues/667/)) + +* Changes *node* option to not assume that all programs must be running in + strict mode. ([#721](https://github.com/jshint/jshint/issues/721/)) + +* Adds new globals for the *browser* option: Element and Uint8ClampedArray. + ([#707](https://github.com/jshint/jshint/issues/707/) and + [#766](https://github.com/jshint/jshint/issues/766/)) + +* Adds new global for the *node* option: DataView. + ([#773](https://github.com/jshint/jshint/issues/773/) and + [#774](https://github.com/jshint/jshint/issues/774/)) + +* Removes option *onecase*. + +* **Adds new directive: exported**. Use `/* exported ... ` for global variables + that are defined in the current file but used elsewhere to prevent + unnecessary *X is defined but never used* warnings. As before, you need to + declare those variables as global in the other files. + + ([#726](https://github.com/jshint/jshint/issues/726/) and + [#659](https://github.com/jshint/jshint/issues/659/)) + +* Removes a warning about missing *break* before *default* when *default* is + the first switch statement + ([#490](https://github.com/jshint/jshint/issues/490/)): + + switch (name) { + default: // No warning here + doSomething(); + break; + case "JSHint": + doSomethingElse(); + } + +* Improves support for [future reserved + keywords](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words#Words_reserved_for_possible_future_use). + JSHint now properly recognizes future reserved keywords both for ES3 and ES5 + environments with their corresponding rules. + ([#674](https://github.com/jshint/jshint/issues/674/)) + +* Changes behavior for *hasOwnProperty* + ([#770](https://github.com/jshint/jshint/issues/770/)): + + var hasOwnProperty = ...; // No warning + var obj = { hasOwnProperty: ... }; // Warning + obj.hasOwnProperty = ...; // Warning + obj['hasOwnProperty'] = ...; // Warning + +* Adds ability to disable option *unused* per function! + ([#639](https://github.com/jshint/jshint/issues/639/)) + + // jshint unused:true + var a; // Warning + + function foo(b) { // No warning + // jshint unused:false + return 1; + } + + foo(); + +Bug fixes: + +* Adds *scope* property to critical errors. + ([#714](https://github.com/jshint/jshint/issues/714/)) +* Fixes a regression bug with option *predef* making all global variables + writeable. ([#665](https://github.com/jshint/jshint/issues/665/)) +* Fixes a bug with JSHint not warning about potential typos on `return o.a = + 1`. ([#670](https://github.com/jshint/jshint/issues/670/)) +* Fixes a bug with *implied* property containing false positive data when + option *undef* is off. ([#668](https://github.com/jshint/jshint/issues/668/)) + + +#### CLI + +* This version **removes support for the JavaScriptCore shell** due to its + limited API. Note that this doesn't mean that JSHint no longer works in + Safari, it simply means that we removed the ability to use jshint via the + command line JSC shell. + +* This version also **removes support for Windows Script Host**. WSH support + was initially added due to Node not working well on Windows but, thanks to + Microsoft engineers, this is no longer true. So everyone is encouraged to use + JSHint with Node. + +* This version relies on ES5 syntax, so if you use JSHint with Rhino, please + make sure you have the latest version: 1.7R4. + +This version includes several improvements to the Node version of JSHint: + +* Adds a new flag, `--verbose`, that changes output to display message codes: + + $ jshint --verbose my.js + my.js: line 7, col 23, Extra comma. (...) (W070) + +* Makes `--config` raise an error if it can't find provided file or if the file + is invalid JSON. ([#691](https://github.com/jshint/jshint/issues/691/)) + +Bug fixes: + +* Fixes a bug with `.jshintignore` globbing not working properly. + ([#777](https://github.com/jshint/jshint/issues/777/) and + [#692](https://github.com/jshint/jshint/issues/692/)) + +* Fixes a bug with JSHint skipping over files with no extensions. + ([#690](https://github.com/jshint/jshint/issues/690/)) + + +#### What's next? + +I plan to test this release candidate for about a week before marking it as +stable and publishing on NPM. And, at the same time, I will be updating the +documentation and the [jshint.com](http://jshint.com/) website. If you notice +any bugs or unexpected backwards-incompatible changes, please file a bug. + +**RC3 is out:** [JSHint 1.0.0 RC3](http://jshint.com/blog/2013-01-01/1-0-0-rc3/). + +Here's how you can install this release candidate: + + $ npm install https://github.com/jshint/jshint/archive/1.0.0-rc1.tar.gz + +For Rhino wrapper, you will need to clone our repo and build jshint-rhino: + + $ node make.js build + $ rhino dist/jshint-rhino.js ... + +#### Contributors + +Thanks to Bernhard K. Weisshuhn, James Allardice, Mike MacCana, Stephen Fry, +Steven Olmsted, Leith Abdulla, Eric Promislow and Vlad Gurdiga for submitting +patches! diff --git a/js/node/node_modules/jshint/README.md b/js/node/node_modules/jshint/README.md index a7b1fc4dcf..10ec3e697d 100644 --- a/js/node/node_modules/jshint/README.md +++ b/js/node/node_modules/jshint/README.md @@ -1,8 +1,9 @@ # JSHint, A Static Code Analysis Tool for JavaScript -\[ [Use it online](http://jshint.com/) • [About](http://jshint.com/about/) • +\[ [Use it online](http://jshint.com/) • [Docs](http://jshint.com/docs/) • [FAQ](http://jshint.com/docs/faq) • -[Install](http://jshint.com/install/) • [Hack](http://jshint.com/hack/) • +[Install](http://jshint.com/install/) • +[Contribute](http://jshint.com/contribute/) • [Blog](http://jshint.com/blog/) • [Twitter](https://twitter.com/jshint/) \] [![NPM version](https://img.shields.io/npm/v/jshint.svg?style=flat)](https://www.npmjs.com/package/jshint) @@ -12,10 +13,33 @@ [![devDependency Status](https://img.shields.io/david/dev/jshint/jshint.svg?style=flat)](https://david-dm.org/jshint/jshint#info=devDependencies) [![Coverage Status](https://img.shields.io/coveralls/jshint/jshint.svg?style=flat)](https://coveralls.io/r/jshint/jshint?branch=master) -JSHint is a community-driven tool to detect errors and potential problems -in JavaScript code. It is very flexible so you can easily adjust it to your -particular coding guidelines and the environment you expect your code to -execute in. +JSHint is a community-driven tool to detect errors and potential problems in +JavaScript code and to enforce your team's coding conventions. It is very +flexible so you can easily adjust it to your particular coding guidelines and +the environment you expect your code to execute in. JSHint is open source and +will always stay this way. + +## Our goal + +The goal of this project is to help JavaScript developers write complex programs +without worrying about typos and language gotchas. + +Any code base eventually becomes huge at some point, and simple mistakes—that +would not show themselves when written—can become show stoppers and waste +hours of debugging. And this is when static code analysis tools come into play +and help developers to spot such problems. JSHint scans a program written in +JavaScript and reports about commonly made mistakes and potential bugs. The +potential problem could be a syntax error, a bug due to implicit type +conversion, a leaking variable or something else. + +Only 15% of all programs linted on [jshint.com](http://jshint.com) pass the +JSHint checks. In all other cases, JSHint finds some red flags that could've +been bugs or potential problems. + +Please note, that while static code analysis tools can spot many different kind +of mistakes, it can't detect if your program is correct, fast or has memory +leaks. You should always combine tools like JSHint with unit and functional +tests as well as with code reviews. ## Reporting a bug @@ -31,25 +55,56 @@ JSHint including but not limited to: Before reporting a bug look around to see if there are any open or closed tickets that cover your issue. And remember the wisdom: pull request > bug report > tweet. -## Issue Priority +## Who uses JSHint? -- *P1:* Something is throwing exceptions; broken JSHint backward compatibility. -- *P2:* Something is not being parsed correctly. -- *P3:* Features that the core team will work on once P2s and P1s are done. -- *P4:* Patches welcome; The request is good, but low priority. +Engineers from these companies and projects use JSHint: +* [Mozilla](https://www.mozilla.org/) +* [Wikipedia](https://wikipedia.org/) +* [Facebook](https://facebook.com/) +* [Twitter](https://twitter.com/) +* [Bootstrap](http://getbootstrap.com/) +* [Disqus](https://disqus.com/) +* [Medium](https://medium.com/) +* [Yahoo!](https://yahoo.com/) +* [SmugMug](http://smugmug.com/) +* [jQuery](http://jquery.com/) +* [PDF.js](http://mozilla.github.io/pdf.js) +* [Coursera](http://coursera.com/) +* [Adobe Brackets](http://brackets.io/) +* [Apache Cordova](http://cordova.io/) +* [RedHat](http://redhat.com/) +* [SoundCloud](http://soundcloud.com/) +* [Nodejitsu](http://nodejitsu.com/) +* [Yelp](https://yelp.com/) +* [Voxer](http://voxer.com/) +* [EnyoJS](http://enyojs.com/) +* [QuickenLoans](http://quickenloans.com/) +* [oDesk](http://www.odesk.com/) +* [Cloud9](http://c9.io/) +* [CodeClimate](https://codeclimate.com/) +* [Pandoo TEK](http://pandootek.com/) +* [Zendesk](http://zendesk.com/) +* [Apache CouchDB](http://couchdb.apache.org/) + +And many more! ## License -JSHint is distributed under the MIT License. One file and one file only -(src/stable/jshint.js) is distributed under the slightly modified MIT License. +Most files are published using [the standard MIT Expat +license](https://www.gnu.org/licenses/license-list.html#Expat). One file, +however, is provided under a slightly modified version of that license. The +so-called [JSON license](https://www.gnu.org/licenses/license-list.html#JSON) +is a non-free license, and unfortunately, we can't change it due to historical +reasons. This license is included as an in-line within the file it concerns. ## The JSHint Team -JSHint is maintained by [Rick Waldron](https://github.com/rwaldron/), [Caitlin -Potter](https://github.com/caitp/), [Mike -Sherov](https://github.com/mikesherov/), and [Mike -Pennisi](https://github.com/jugglinmike/). +JSHint is currently maintained by [Rick Waldron](https://github.com/rwaldron/), +[Caitlin Potter](https://github.com/caitp/), [Mike +Sherov](https://github.com/mikesherov/), [Mike +Pennisi](https://github.com/jugglinmike/), and [Luke +Page](https://github.com/lukeapage). ## Thank you! diff --git a/js/node/node_modules/jshint/dist/jshint-rhino.js b/js/node/node_modules/jshint/dist/jshint-rhino.js index 2dc9808496..a24d930d26 100755 --- a/js/node/node_modules/jshint/dist/jshint-rhino.js +++ b/js/node/node_modules/jshint/dist/jshint-rhino.js @@ -1,6 +1,6 @@ #!/usr/bin/env rhino var window = {}; -/*! 2.7.0 */ +/*! 2.9.2 */ var JSHINT; if (typeof window === 'undefined') window = {}; (function () { @@ -1488,10 +1488,10 @@ function now() { (function (global){ /** * @license - * lodash 3.6.0 (Custom Build) + * lodash 3.7.0 (Custom Build) * Build: `lodash modern -d -o ./index.js` * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.2 + * Based on Underscore.js 1.8.3 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ @@ -1501,7 +1501,7 @@ function now() { var undefined; /** Used as the semantic version number. */ - var VERSION = '3.6.0'; + var VERSION = '3.7.0'; /** Used to compose bitmasks for wrapper metadata. */ var BIND_FLAG = 1, @@ -1575,30 +1575,10 @@ function now() { reEvaluate = /<%([\s\S]+?)%>/g, reInterpolate = /<%=([\s\S]+?)%>/g; - /** - * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - */ - var reComboMarks = /[\u0300-\u036f\ufe20-\ufe23]/g; - - /** - * Used to match [ES template delimiters](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components). - */ - var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - - /** Used to match `RegExp` flags from their coerced string values. */ - var reFlags = /\w*$/; - - /** Used to detect hexadecimal string values. */ - var reHexPrefix = /^0[xX]/; - - /** Used to detect host constructors (Safari > 5). */ - var reHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ - var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; - - /** Used to ensure capturing order of template delimiters. */ - var reNoMatch = /($^)/; + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]+|(["'])(?:(?!\1)[^\n\\]|\\.)*?)\1\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; /** * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). @@ -1608,6 +1588,30 @@ function now() { var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, reHasRegExpChars = RegExp(reRegExpChars.source); + /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ + var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to match [ES template delimiters](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components). */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect hexadecimal string values. */ + var reHasHexPrefix = /^0[xX]/; + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ + var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + /** Used to match unescaped characters in compiled string literals. */ var reUnescapedString = /['\n\r\u2028\u2029\\]/g; @@ -1745,7 +1749,7 @@ function now() { var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; /** Detect free variable `global` from Node.js. */ - var freeGlobal = freeExports && freeModule && typeof global == 'object' && global; + var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; /** Detect free variable `self`. */ var freeSelf = objectTypes[typeof self] && self && self.Object && self; @@ -1780,10 +1784,10 @@ function now() { var valIsReflexive = value === value, othIsReflexive = other === other; - if (value > other || !valIsReflexive || (typeof value == 'undefined' && othIsReflexive)) { + if (value > other || !valIsReflexive || (value === undefined && othIsReflexive)) { return 1; } - if (value < other || !othIsReflexive || (typeof other == 'undefined' && valIsReflexive)) { + if (value < other || !othIsReflexive || (other === undefined && valIsReflexive)) { return -1; } } @@ -1926,7 +1930,7 @@ function now() { * Used by `_.sortByOrder` to compare multiple properties of each element * in a collection and stable sort them in the following order: * - * If orders is unspecified, sort in ascending order for all properties. + * If `orders` is unspecified, sort in ascending order for all properties. * Otherwise, for each property, sort in ascending order if its corresponding value in * orders is true, and descending order if false. * @@ -2203,9 +2207,6 @@ function now() { /** Used to resolve the decompiled source of functions. */ var fnToString = Function.prototype.toString; - /** Used to the length of n-tuples for `_.unzip`. */ - var getLength = baseProperty('length'); - /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; @@ -2222,7 +2223,7 @@ function now() { var oldDash = context._; /** Used to detect if a method is native. */ - var reNative = RegExp('^' + + var reIsNative = RegExp('^' + escapeRegExp(objToString) .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); @@ -2233,8 +2234,10 @@ function now() { ceil = Math.ceil, clearTimeout = context.clearTimeout, floor = Math.floor, + getOwnPropertySymbols = isNative(getOwnPropertySymbols = Object.getOwnPropertySymbols) && getOwnPropertySymbols, getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, push = arrayProto.push, + preventExtensions = isNative(Object.preventExtensions = Object.preventExtensions) && preventExtensions, propertyIsEnumerable = objectProto.propertyIsEnumerable, Set = isNative(Set = context.Set) && Set, setTimeout = context.setTimeout, @@ -2254,6 +2257,22 @@ function now() { return result; }()); + /** Used as `baseAssign`. */ + var nativeAssign = (function() { + // Avoid `Object.assign` in Firefox 34-37 which have an early implementation + // with a now defunct try/catch behavior. See https://bugzilla.mozilla.org/show_bug.cgi?id=1103344 + // for more details. + // + // Use `Object.preventExtensions` on a plain object instead of simply using + // `Object('x')` because Chrome and IE fail to throw an error when attempting + // to assign values to readonly indexes of strings in strict mode. + var object = { '1': 0 }, + func = preventExtensions && isNative(func = Object.assign) && func; + + try { func(preventExtensions(object), 'xo'); } catch(e) {} + return !object[1] && func; + }()); + /* Native method references for those with the same name as other `lodash` methods. */ var nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, @@ -2331,8 +2350,8 @@ function now() { * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`, * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`, * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`, - * `keysIn`, `map`, `mapValues`, `matches`, `matchesProperty`, `memoize`, `merge`, - * `mixin`, `negate`, `noop`, `omit`, `once`, `pairs`, `partial`, `partialRight`, + * `keysIn`, `map`, `mapValues`, `matches`, `matchesProperty`, `memoize`, + * `merge`, `mixin`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `reverse`, * `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, @@ -2346,15 +2365,15 @@ function now() { * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `has`, * `identity`, `includes`, `indexOf`, `inRange`, `isArguments`, `isArray`, - * `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, - * `isFinite`,`isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`, - * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, - * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`, - * `noConflict`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, - * `random`, `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`, - * `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, - * `startCase`, `startsWith`, `sum`, `template`, `trim`, `trimLeft`, - * `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words` + * `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, `isFinite` + * `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, + * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, + * `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`, `noConflict`, + * `noop`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, + * `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, `startsWith`, + * `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, `unescape`, + * `uniqueId`, `value`, and `words` * * The wrapper method `sample` will return a wrapped value when `n` is provided, * otherwise an unwrapped value is returned. @@ -2369,8 +2388,8 @@ function now() { * var wrapped = _([1, 2, 3]); * * // returns an unwrapped value - * wrapped.reduce(function(sum, n) { - * return sum + n; + * wrapped.reduce(function(total, n) { + * return total + n; * }); * // => 6 * @@ -2430,6 +2449,12 @@ function now() { var support = lodash.support = {}; (function(x) { + var Ctor = function() { this.x = x; }, + object = { '0': x, 'length': x }, + props = []; + + Ctor.prototype = { 'valueOf': x, 'y': x }; + for (var key in new Ctor) { props.push(key); } /** * Detect if functions can be decompiled by `Function#toString` @@ -2467,8 +2492,8 @@ function now() { * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat * `arguments` object indexes as non-enumerable and fail `hasOwnProperty` - * checks for indexes that exceed their function's formal parameters with - * associated values of `0`. + * checks for indexes that exceed the number of function parameters and + * whose associated argument values are `0`. * * @memberOf _.support * @type boolean @@ -2478,7 +2503,7 @@ function now() { } catch(e) { support.nonEnumArgs = true; } - }(0, 0)); + }(1, 0)); /** * By default, the template delimiters used by lodash are like those in @@ -2725,7 +2750,7 @@ function now() { } /** - * Adds `value` to `key` of the cache. + * Sets `value` to `key` of the cache. * * @private * @name set @@ -3058,13 +3083,13 @@ function now() { * @returns {*} Returns the value to assign to the destination object. */ function assignDefaults(objectValue, sourceValue) { - return typeof objectValue == 'undefined' ? sourceValue : objectValue; + return objectValue === undefined ? sourceValue : objectValue; } /** * Used by `_.template` to customize its `_.assign` use. * - * **Note:** This method is like `assignDefaults` except that it ignores + * **Note:** This function is like `assignDefaults` except that it ignores * inherited property values when checking if a property is `undefined`. * * @private @@ -3075,26 +3100,26 @@ function now() { * @returns {*} Returns the value to assign to the destination object. */ function assignOwnDefaults(objectValue, sourceValue, key, object) { - return (typeof objectValue == 'undefined' || !hasOwnProperty.call(object, key)) + return (objectValue === undefined || !hasOwnProperty.call(object, key)) ? sourceValue : objectValue; } /** - * The base implementation of `_.assign` without support for argument juggling, - * multiple sources, and `this` binding `customizer` functions. + * A specialized version of `_.assign` for customizing assigned values without + * support for argument juggling, multiple sources, and `this` binding `customizer` + * functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. - * @param {Function} [customizer] The function to customize assigning values. - * @returns {Object} Returns the destination object. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. */ - function baseAssign(object, source, customizer) { + function assignWith(object, source, customizer) { var props = keys(source); - if (!customizer) { - return baseCopy(source, object, props); - } + push.apply(props, getSymbols(source)); + var index = -1, length = props.length; @@ -3104,7 +3129,7 @@ function now() { result = customizer(value, source[key], key, object, source); if ((result === result ? (result !== value) : (value === value)) || - (typeof value == 'undefined' && !(key in object))) { + (value === undefined && !(key in object))) { object[key] = result; } } @@ -3112,12 +3137,27 @@ function now() { } /** - * The base implementation of `_.at` without support for strings and individual - * key arguments. + * The base implementation of `_.assign` without support for argument juggling, + * multiple sources, and `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + var baseAssign = nativeAssign || function(object, source) { + return source == null + ? object + : baseCopy(source, getSymbols(source), baseCopy(source, keys(source), object)); + }; + + /** + * The base implementation of `_.at` without support for string collections + * and individual key arguments. * * @private * @param {Array|Object} collection The collection to iterate over. - * @param {number[]|string[]} [props] The property names or indexes of elements to pick. + * @param {number[]|string[]} props The property names or indexes of elements to pick. * @returns {Array} Returns the new array of picked elements. */ function baseAt(collection, props) { @@ -3130,7 +3170,6 @@ function now() { while(++index < propsLength) { var key = props[index]; if (isArr) { - key = parseFloat(key); result[index] = isIndex(key, length) ? collection[key] : undefined; } else { result[index] = collection[key]; @@ -3140,19 +3179,17 @@ function now() { } /** - * Copies the properties of `source` to `object`. + * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. - * @param {Object} [object={}] The object to copy properties to. * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. * @returns {Object} Returns `object`. */ - function baseCopy(source, object, props) { - if (!props) { - props = object; - object = {}; - } + function baseCopy(source, props, object) { + object || (object = {}); + var index = -1, length = props.length; @@ -3176,7 +3213,7 @@ function now() { function baseCallback(func, thisArg, argCount) { var type = typeof func; if (type == 'function') { - return typeof thisArg == 'undefined' + return thisArg === undefined ? func : bindCallback(func, thisArg, argCount); } @@ -3186,9 +3223,9 @@ function now() { if (type == 'object') { return baseMatches(func); } - return typeof thisArg == 'undefined' - ? baseProperty(func + '') - : baseMatchesProperty(func + '', thisArg); + return thisArg === undefined + ? property(func) + : baseMatchesProperty(func, thisArg); } /** @@ -3210,7 +3247,7 @@ function now() { if (customizer) { result = object ? customizer(value, key, object) : customizer(value); } - if (typeof result != 'undefined') { + if (result !== undefined) { return result; } if (!isObject(value)) { @@ -3229,7 +3266,7 @@ function now() { if (tag == objectTag || tag == argsTag || (isFunc && !object)) { result = initCloneObject(isFunc ? {} : value); if (!isDeep) { - return baseCopy(value, result, keys(value)); + return baseAssign(result, value); } } else { return cloneableTags[tag] @@ -3400,7 +3437,7 @@ function now() { if (start < 0) { start = -start > length ? 0 : (length + start); } - end = (typeof end == 'undefined' || end > length) ? length : (+end || 0); + end = (end === undefined || end > length) ? length : (+end || 0); if (end < 0) { end += length; } @@ -3497,7 +3534,7 @@ function now() { /** * The base implementation of `baseForIn` and `baseForOwn` which iterates * over `object` properties returned by `keysFunc` invoking `iteratee` for - * each property. Iterator functions may exit iteration early by explicitly + * each property. Iteratee functions may exit iteration early by explicitly * returning `false`. * * @private @@ -3583,6 +3620,32 @@ function now() { return result; } + /** + * The base implementation of `get` without support for string paths + * and default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path of the property to get. + * @param {string} [pathKey] The key representation of path. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path, pathKey) { + if (object == null) { + return; + } + if (pathKey !== undefined && pathKey in toObject(object)) { + path = [pathKey]; + } + var index = -1, + length = path.length; + + while (object != null && ++index < length) { + var result = object = object[path[index]]; + } + return result; + } + /** * The base implementation of `_.isEqual` without support for `this` binding * `customizer` functions. @@ -3651,27 +3714,23 @@ function now() { othIsArr = isTypedArray(other); } } - var objIsObj = (objTag == objectTag || (isLoose && objTag == funcTag)), - othIsObj = (othTag == objectTag || (isLoose && othTag == funcTag)), + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, isSameTag = objTag == othTag; if (isSameTag && !(objIsArr || objIsObj)) { return equalByTag(object, other, objTag); } - if (isLoose) { - if (!isSameTag && !(objIsObj && othIsObj)) { - return false; - } - } else { + if (!isLoose) { var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); if (valWrapped || othWrapped) { return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); } - if (!isSameTag) { - return false; - } + } + if (!isSameTag) { + return false; } // Assume cyclic values are equal. // For more information on detecting circular references see https://es5.github.io/#JO. @@ -3728,10 +3787,10 @@ function now() { srcValue = values[index]; if (noCustomizer && strictCompareFlags[index]) { - var result = typeof objValue != 'undefined' || (key in object); + var result = objValue !== undefined || (key in object); } else { result = customizer ? customizer(objValue, srcValue, key) : undefined; - if (typeof result == 'undefined') { + if (result === undefined) { result = baseIsEqual(srcValue, objValue, customizer, true); } } @@ -3752,9 +3811,12 @@ function now() { * @returns {Array} Returns the new mapped array. */ function baseMap(collection, iteratee) { - var result = []; + var index = -1, + length = getLength(collection), + result = isLength(length) ? Array(length) : []; + baseEach(collection, function(value, key, collection) { - result.push(iteratee(value, key, collection)); + result[++index] = iteratee(value, key, collection); }); return result; } @@ -3779,8 +3841,10 @@ function now() { if (isStrictComparable(value)) { return function(object) { - return object != null && object[key] === value && - (typeof value != 'undefined' || (key in toObject(object))); + if (object == null) { + return false; + } + return object[key] === value && (value !== undefined || (key in toObject(object))); }; } } @@ -3798,23 +3862,37 @@ function now() { } /** - * The base implementation of `_.matchesProperty` which does not coerce `key` - * to a string. + * The base implementation of `_.matchesProperty` which does not which does + * not clone `value`. * * @private - * @param {string} key The key of the property to get. + * @param {string} path The path of the property to get. * @param {*} value The value to compare. * @returns {Function} Returns the new function. */ - function baseMatchesProperty(key, value) { - if (isStrictComparable(value)) { - return function(object) { - return object != null && object[key] === value && - (typeof value != 'undefined' || (key in toObject(object))); - }; - } + function baseMatchesProperty(path, value) { + var isArr = isArray(path), + isCommon = isKey(path) && isStrictComparable(value), + pathKey = (path + ''); + + path = toPath(path); return function(object) { - return object != null && baseIsEqual(value, object[key], null, true); + if (object == null) { + return false; + } + var key = pathKey; + object = toObject(object); + if ((isArr || !isCommon) && !(key in object)) { + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + if (object == null) { + return false; + } + key = last(path); + object = toObject(object); + } + return object[key] === value + ? (value !== undefined || (key in object)) + : baseIsEqual(value, object[key], null, true); }; } @@ -3828,29 +3906,39 @@ function now() { * @param {Function} [customizer] The function to customize merging properties. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates values with source counterparts. - * @returns {Object} Returns the destination object. + * @returns {Object} Returns `object`. */ function baseMerge(object, source, customizer, stackA, stackB) { if (!isObject(object)) { return object; } var isSrcArr = isLength(source.length) && (isArray(source) || isTypedArray(source)); - (isSrcArr ? arrayEach : baseForOwn)(source, function(srcValue, key, source) { + if (!isSrcArr) { + var props = keys(source); + push.apply(props, getSymbols(source)); + } + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } if (isObjectLike(srcValue)) { stackA || (stackA = []); stackB || (stackB = []); - return baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); + baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); } - var value = object[key], - result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = typeof result == 'undefined'; + else { + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; - if (isCommon) { - result = srcValue; - } - if ((isSrcArr || typeof result != 'undefined') && - (isCommon || (result === result ? (result !== value) : (value === value)))) { - object[key] = result; + if (isCommon) { + result = srcValue; + } + if ((isSrcArr || result !== undefined) && + (isCommon || (result === result ? (result !== value) : (value === value)))) { + object[key] = result; + } } }); return object; @@ -3883,14 +3971,14 @@ function now() { } var value = object[key], result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = typeof result == 'undefined'; + isCommon = result === undefined; if (isCommon) { result = srcValue; if (isLength(srcValue.length) && (isArray(srcValue) || isTypedArray(srcValue))) { result = isArray(value) ? value - : ((value && value.length) ? arrayCopy(value) : []); + : (getLength(value) ? arrayCopy(value) : []); } else if (isPlainObject(srcValue) || isArguments(srcValue)) { result = isArguments(value) @@ -3915,7 +4003,7 @@ function now() { } /** - * The base implementation of `_.property` which does not coerce `key` to a string. + * The base implementation of `_.property` without support for deep paths. * * @private * @param {string} key The key of the property to get. @@ -3927,6 +4015,42 @@ function now() { }; } + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new function. + */ + function basePropertyDeep(path) { + var pathKey = (path + ''); + path = toPath(path); + return function(object) { + return baseGet(object, path, pathKey); + }; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * index arguments and capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = indexes.length; + while (length--) { + var index = parseFloat(indexes[length]); + if (index != previous && isIndex(index)) { + var previous = index; + splice.call(array, index, 1); + } + } + return array; + } + /** * The base implementation of `_.random` without support for argument juggling * and returning floating-point numbers. @@ -3993,7 +4117,7 @@ function now() { if (start < 0) { start = -start > length ? 0 : (length + start); } - end = (typeof end == 'undefined' || end > length) ? length : (+end || 0); + end = (end === undefined || end > length) ? length : (+end || 0); if (end < 0) { end += length; } @@ -4052,23 +4176,19 @@ function now() { * * @private * @param {Array|Object|string} collection The collection to iterate over. - * @param {string[]} props The property names to sort by. - * @param {boolean[]} orders The sort orders of `props`. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} orders The sort orders of `iteratees`. * @returns {Array} Returns the new sorted array. */ - function baseSortByOrder(collection, props, orders) { - var index = -1, - length = collection.length, - result = isLength(length) ? Array(length) : []; + function baseSortByOrder(collection, iteratees, orders) { + var callback = getCallback(), + index = -1; - baseEach(collection, function(value) { - var length = props.length, - criteria = Array(length); + iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); }); - while (length--) { - criteria[length] = value == null ? undefined : value[props[length]]; - } - result[++index] = { 'criteria': criteria, 'index': index, 'value': value }; + var result = baseMap(collection, function(value) { + var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; }); return baseSortBy(result, function(object, other) { @@ -4148,7 +4268,7 @@ function now() { /** * The base implementation of `_.values` and `_.valuesIn` which creates an * array of `object` property values corresponding to the property names - * returned by `keysFunc`. + * of `props`. * * @private * @param {Object} object The object to query. @@ -4265,7 +4385,7 @@ function now() { var low = 0, high = array ? array.length : 0, valIsNaN = value !== value, - valIsUndef = typeof value == 'undefined'; + valIsUndef = value === undefined; while (low < high) { var mid = floor((low + high) / 2), @@ -4275,7 +4395,7 @@ function now() { if (valIsNaN) { var setLow = isReflexive || retHighest; } else if (valIsUndef) { - setLow = isReflexive && (retHighest || typeof computed != 'undefined'); + setLow = isReflexive && (retHighest || computed !== undefined); } else { setLow = retHighest ? (computed <= value) : (computed < value); } @@ -4302,7 +4422,7 @@ function now() { if (typeof func != 'function') { return identity; } - if (typeof thisArg == 'undefined') { + if (thisArg === undefined) { return func; } switch (argCount) { @@ -4462,38 +4582,32 @@ function now() { * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { - return function() { - var args = arguments, - length = args.length, - object = args[0]; + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 && sources[length - 2], + guard = length > 2 && sources[2], + thisArg = length > 1 && sources[length - 1]; - if (length < 2 || object == null) { - return object; - } - var customizer = args[length - 2], - thisArg = args[length - 1], - guard = args[3]; - - if (length > 3 && typeof customizer == 'function') { + if (typeof customizer == 'function') { customizer = bindCallback(customizer, thisArg, 5); length -= 2; } else { - customizer = (length > 2 && typeof thisArg == 'function') ? thisArg : null; + customizer = typeof thisArg == 'function' ? thisArg : null; length -= (customizer ? 1 : 0); } - if (guard && isIterateeCall(args[1], args[2], guard)) { - customizer = length == 3 ? null : customizer; - length = 2; + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? null : customizer; + length = 1; } - var index = 0; while (++index < length) { - var source = args[index]; + var source = sources[index]; if (source) { assigner(object, source, customizer); } } return object; - }; + }); } /** @@ -4506,7 +4620,7 @@ function now() { */ function createBaseEach(eachFunc, fromRight) { return function(collection, iteratee) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; if (!isLength(length)) { return eachFunc(collection, iteratee); } @@ -4783,7 +4897,7 @@ function now() { */ function createForEach(arrayFunc, eachFunc) { return function(collection, iteratee, thisArg) { - return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection)) + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) ? arrayFunc(collection, iteratee) : eachFunc(collection, bindCallback(iteratee, thisArg, 3)); }; @@ -4798,7 +4912,7 @@ function now() { */ function createForIn(objectFunc) { return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || typeof thisArg != 'undefined') { + if (typeof iteratee != 'function' || thisArg !== undefined) { iteratee = bindCallback(iteratee, thisArg, 3); } return objectFunc(object, iteratee, keysIn); @@ -4814,7 +4928,7 @@ function now() { */ function createForOwn(objectFunc) { return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || typeof thisArg != 'undefined') { + if (typeof iteratee != 'function' || thisArg !== undefined) { iteratee = bindCallback(iteratee, thisArg, 3); } return objectFunc(object, iteratee); @@ -4861,7 +4975,7 @@ function now() { function createReduce(arrayFunc, eachFunc) { return function(collection, iteratee, accumulator, thisArg) { var initFromArray = arguments.length < 3; - return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection)) + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) ? arrayFunc(collection, iteratee, accumulator, initFromArray) : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); }; @@ -5130,7 +5244,7 @@ function now() { ? customizer(othValue, arrValue, index) : customizer(arrValue, othValue, index); } - if (typeof result == 'undefined') { + if (result === undefined) { // Recursively compare arrays (susceptible to call stack limits). if (isLoose) { var othIndex = othLength; @@ -5229,7 +5343,7 @@ function now() { ? customizer(othValue, objValue, key) : customizer(objValue, othValue, key); } - if (typeof result == 'undefined') { + if (result === undefined) { // Recursively compare objects (susceptible to call stack limits). result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB); } @@ -5354,6 +5468,29 @@ function now() { return collection ? result(collection, target, fromIndex) : result; } + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * in Safari on iOS 8.1 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + /** + * Creates an array of the own symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !getOwnPropertySymbols ? constant([]) : function(object) { + return getOwnPropertySymbols(toObject(object)); + }; + /** * Gets the view, applying any `transforms` to the `start` and `end` positions. * @@ -5422,7 +5559,6 @@ function now() { * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * - * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. @@ -5456,6 +5592,25 @@ function now() { return result; } + /** + * Invokes the method at `path` on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function invokePath(object, path, args) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + } + var func = object == null ? object : object[path]; + return func == null ? undefined : func.apply(object, args); + } + /** * Checks if `value` is a valid array-like index. * @@ -5485,7 +5640,7 @@ function now() { } var type = typeof index; if (type == 'number') { - var length = object.length, + var length = getLength(object), prereq = isLength(length) && isIndex(index, length); } else { prereq = type == 'string' && index in object; @@ -5497,6 +5652,26 @@ function now() { return false; } + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + var type = typeof value; + if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { + return true; + } + if (isArray(value)) { + return false; + } + var result = !reIsDeepProp.test(value); + return result || (object != null && value in toObject(object)); + } + /** * Checks if `func` has a lazy counterpart. * @@ -5606,7 +5781,7 @@ function now() { /** * A specialized version of `_.pick` that picks `object` properties specified - * by the `props` array. + * by `props`. * * @private * @param {Object} object The source object. @@ -5732,7 +5907,7 @@ function now() { baseForIn(value, function(subValue, key) { result = key; }); - return typeof result == 'undefined' || hasOwnProperty.call(value, result); + return result === undefined || hasOwnProperty.call(value, result); } /** @@ -5740,7 +5915,7 @@ function now() { * own enumerable property names of `object`. * * @private - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function shimKeys(object) { @@ -5775,7 +5950,7 @@ function now() { if (value == null) { return []; } - if (!isLength(value.length)) { + if (!isLength(getLength(value))) { return values(value); } return isObject(value) ? value : Object(value); @@ -5792,6 +5967,24 @@ function now() { return isObject(value) ? value : Object(value); } + /** + * Converts `value` to property path array if it is not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array} Returns the property path array. + */ + function toPath(value) { + if (isArray(value)) { + return value; + } + var result = []; + baseToString(value).replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + } + /** * Creates a clone of `wrapper`. * @@ -6376,7 +6569,8 @@ function now() { argsLength = arguments.length, caches = [], indexOf = getIndexOf(), - isCommon = indexOf == baseIndexOf; + isCommon = indexOf == baseIndexOf, + result = []; while (++argsIndex < argsLength) { var value = arguments[argsIndex]; @@ -6386,10 +6580,12 @@ function now() { } } argsLength = args.length; + if (argsLength < 2) { + return result; + } var array = args[0], index = -1, length = array ? array.length : 0, - result = [], seen = caches[0]; outer: @@ -6557,17 +6753,8 @@ function now() { array || (array = []); indexes = baseFlatten(indexes); - var length = indexes.length, - result = baseAt(array, indexes); - - indexes.sort(baseCompareAscending); - while (length--) { - var index = parseFloat(indexes[length]); - if (index != previous && isIndex(index)) { - var previous = index; - splice.call(array, index, 1); - } - } + var result = baseAt(array, indexes); + basePullAt(array, indexes.sort(baseCompareAscending)); return result; }); @@ -6611,19 +6798,23 @@ function now() { * // => [2, 4] */ function remove(array, predicate, thisArg) { + var result = []; + if (!(array && array.length)) { + return result; + } var index = -1, - length = array ? array.length : 0, - result = []; + indexes = [], + length = array.length; predicate = getCallback(predicate, thisArg, 3); while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result.push(value); - splice.call(array, index--, 1); - length--; + indexes.push(index); } } + basePullAt(array, indexes); return result; } @@ -6648,7 +6839,7 @@ function now() { /** * Creates a slice of `array` from `start` up to, but not including, `end`. * - * **Note:** This function is used instead of `Array#slice` to support node + * **Note:** This method is used instead of `Array#slice` to support node * lists in IE < 9 and to ensure dense arrays are returned. * * @static @@ -6947,12 +7138,13 @@ function now() { }); /** - * Creates a duplicate-value-free version of an array using `SameValueZero` - * for equality comparisons. Providing `true` for `isSorted` performs a faster - * search algorithm for sorted arrays. If an iteratee function is provided it - * is invoked for each value in the array to generate the criterion by which - * uniqueness is computed. The `iteratee` is bound to `thisArg` and invoked - * with three arguments: (value, index, array). + * Creates a duplicate-free version of an array, using `SameValueZero` for + * equality comparisons, in which only the first occurence of each element + * is kept. Providing `true` for `isSorted` performs a faster search algorithm + * for sorted arrays. If an iteratee function is provided it is invoked for + * each element in the array to generate the criterion by which uniqueness + * is computed. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, array). * * If a property name is provided for `iteratee` the created `_.property` * style callback returns the property value of the given element. @@ -6980,8 +7172,8 @@ function now() { * @returns {Array} Returns the new duplicate-value-free array. * @example * - * _.uniq([1, 2, 1]); - * // => [1, 2] + * _.uniq([2, 1, 2]); + * // => [2, 1] * * // using `isSorted` * _.uniq([1, 1, 2], true); @@ -7431,7 +7623,7 @@ function now() { * // => ['barney', 'pebbles'] */ var at = restParam(function(collection, props) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; if (isLength(length)) { collection = toIterable(collection); } @@ -7536,7 +7728,7 @@ function now() { if (thisArg && isIterateeCall(collection, predicate, thisArg)) { predicate = null; } - if (typeof predicate != 'function' || typeof thisArg != 'undefined') { + if (typeof predicate != 'function' || thisArg !== undefined) { predicate = getCallback(predicate, thisArg, 3); } return func(collection, predicate); @@ -7706,10 +7898,10 @@ function now() { /** * Iterates over elements of `collection` invoking `iteratee` for each element. * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). Iterator functions may exit iteration early + * (value, index|key, collection). Iteratee functions may exit iteration early * by explicitly returning `false`. * - * **Note:** As with other "Collections" methods, objects with a `length` property + * **Note:** As with other "Collections" methods, objects with a "length" property * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` * may be used for object iteration. * @@ -7839,7 +8031,7 @@ function now() { * // => true */ function includes(collection, target, fromIndex, guard) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; if (!isLength(length)) { collection = values(collection); length = collection.length; @@ -7908,16 +8100,16 @@ function now() { }); /** - * Invokes the method named by `methodName` on each element in `collection`, - * returning an array of the results of each invoked method. Any additional - * arguments are provided to each invoked method. If `methodName` is a function - * it is invoked for, and `this` bound to, each element in `collection`. + * Invokes the method at `path` on each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `methodName` is a function it is + * invoked for, and `this` bound to, each element in `collection`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|string} methodName The name of the method to invoke or + * @param {Array|Function|string} path The path of the method to invoke or * the function invoked per iteration. * @param {...*} [args] The arguments to invoke the method with. * @returns {Array} Returns the array of results. @@ -7929,15 +8121,16 @@ function now() { * _.invoke([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ - var invoke = restParam(function(collection, methodName, args) { + var invoke = restParam(function(collection, path, args) { var index = -1, - isFunc = typeof methodName == 'function', - length = collection ? collection.length : 0, + isFunc = typeof path == 'function', + isProp = isKey(path), + length = getLength(collection), result = isLength(length) ? Array(length) : []; baseEach(collection, function(value) { - var func = isFunc ? methodName : (value != null && value[methodName]); - result[++index] = func ? func.apply(value, args) : undefined; + var func = isFunc ? path : (isProp && value != null && value[path]); + result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); }); return result; }); @@ -7974,7 +8167,6 @@ function now() { * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. - * create a `_.property` or `_.matches` style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new mapped array. * @example @@ -8068,13 +8260,13 @@ function now() { }, function() { return [[], []]; }); /** - * Gets the value of `key` from all elements in `collection`. + * Gets the property value of `path` from all elements in `collection`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {string} key The key of the property to pluck. + * @param {Array|string} path The path of the property to pluck. * @returns {Array} Returns the property values. * @example * @@ -8090,8 +8282,8 @@ function now() { * _.pluck(userIndex, 'age'); * // => [36, 40] (iteration order is not guaranteed) */ - function pluck(collection, key) { - return map(collection, baseProperty(key)); + function pluck(collection, path) { + return map(collection, property(path)); } /** @@ -8119,8 +8311,8 @@ function now() { * @returns {*} Returns the accumulated value. * @example * - * _.reduce([1, 2], function(sum, n) { - * return sum + n; + * _.reduce([1, 2], function(total, n) { + * return total + n; * }); * // => 3 * @@ -8292,7 +8484,7 @@ function now() { * // => 7 */ function size(collection) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; return isLength(length) ? length : keys(collection).length; } @@ -8350,7 +8542,7 @@ function now() { if (thisArg && isIterateeCall(collection, predicate, thisArg)) { predicate = null; } - if (typeof predicate != 'function' || typeof thisArg != 'undefined') { + if (typeof predicate != 'function' || thisArg !== undefined) { predicate = getCallback(predicate, thisArg, 3); } return func(collection, predicate); @@ -8378,9 +8570,8 @@ function now() { * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|Function|Object|string} [iteratee=_.identity] The function - * invoked per iteration. If a property name or an object is provided it is - * used to create a `_.property` or `_.matches` style callback respectively. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new sorted array. * @example @@ -8409,104 +8600,112 @@ function now() { if (collection == null) { return []; } - var index = -1, - length = collection.length, - result = isLength(length) ? Array(length) : []; - if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { iteratee = null; } + var index = -1; iteratee = getCallback(iteratee, thisArg, 3); - baseEach(collection, function(value, key, collection) { - result[++index] = { 'criteria': iteratee(value, key, collection), 'index': index, 'value': value }; + + var result = baseMap(collection, function(value, key, collection) { + return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; }); return baseSortBy(result, compareAscending); } /** - * This method is like `_.sortBy` except that it sorts by property names - * instead of an iteratee function. + * This method is like `_.sortBy` except that it can sort by multiple iteratees + * or property names. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(string|string[])} props The property names to sort by, - * specified as individual property names or arrays of property names. + * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees + * The iteratees to sort by, specified as individual values or arrays of values. * @returns {Array} Returns the new sorted array. * @example * * var users = [ + * { 'user': 'fred', 'age': 48 }, * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 26 }, - * { 'user': 'fred', 'age': 30 } + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 34 } * ]; * * _.map(_.sortByAll(users, ['user', 'age']), _.values); - * // => [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]] + * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] + * + * _.map(_.sortByAll(users, 'user', function(chr) { + * return Math.floor(chr.age / 10); + * }), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] */ - function sortByAll() { - var args = arguments, - collection = args[0], - guard = args[3], - index = 0, - length = args.length - 1; - + var sortByAll = restParam(function(collection, iteratees) { if (collection == null) { return []; } - var props = Array(length); - while (index < length) { - props[index] = args[++index]; + var guard = iteratees[2]; + if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { + iteratees.length = 1; } - if (guard && isIterateeCall(args[1], args[2], guard)) { - props = args[1]; - } - return baseSortByOrder(collection, baseFlatten(props), []); - } + return baseSortByOrder(collection, baseFlatten(iteratees), []); + }); /** * This method is like `_.sortByAll` except that it allows specifying the - * sort orders of the property names to sort by. A truthy value in `orders` - * will sort the corresponding property name in ascending order while a - * falsey value will sort it in descending order. + * sort orders of the iteratees to sort by. A truthy value in `orders` will + * sort the corresponding property name in ascending order while a falsey + * value will sort it in descending order. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {string[]} props The property names to sort by. - * @param {boolean[]} orders The sort orders of `props`. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} orders The sort orders of `iteratees`. * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. * @returns {Array} Returns the new sorted array. * @example * * var users = [ - * { 'user': 'barney', 'age': 26 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 30 } + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 36 } * ]; * * // sort by `user` in ascending order and by `age` in descending order * _.map(_.sortByOrder(users, ['user', 'age'], [true, false]), _.values); - * // => [['barney', 36], ['barney', 26], ['fred', 40], ['fred', 30]] + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] */ - function sortByOrder(collection, props, orders, guard) { + function sortByOrder(collection, iteratees, orders, guard) { if (collection == null) { return []; } - if (guard && isIterateeCall(props, orders, guard)) { + if (guard && isIterateeCall(iteratees, orders, guard)) { orders = null; } - if (!isArray(props)) { - props = props == null ? [] : [props]; + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; } if (!isArray(orders)) { orders = orders == null ? [] : [orders]; } - return baseSortByOrder(collection, props, orders); + return baseSortByOrder(collection, iteratees, orders); } /** @@ -8659,7 +8858,8 @@ function now() { return function() { if (--n > 0) { result = func.apply(this, arguments); - } else { + } + if (n <= 1) { func = null; } return result; @@ -8674,7 +8874,7 @@ function now() { * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for partially applied arguments. * - * **Note:** Unlike native `Function#bind` this method does not set the `length` + * **Note:** Unlike native `Function#bind` this method does not set the "length" * property of bound functions. * * @static @@ -8716,7 +8916,7 @@ function now() { * of method names. If no method names are provided all enumerable function * properties, own and inherited, of `object` are bound. * - * **Note:** This method does not set the `length` property of bound functions. + * **Note:** This method does not set the "length" property of bound functions. * * @static * @memberOf _ @@ -8757,7 +8957,7 @@ function now() { * * This method differs from `_.bind` by allowing bound functions to reference * methods that may be redefined or don't yet exist. - * See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern) + * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) * for more details. * * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic @@ -8814,7 +9014,7 @@ function now() { * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for provided arguments. * - * **Note:** This method does not set the `length` property of curried functions. + * **Note:** This method does not set the "length" property of curried functions. * * @static * @memberOf _ @@ -8853,7 +9053,7 @@ function now() { * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for provided arguments. * - * **Note:** This method does not set the `length` property of curried functions. + * **Note:** This method does not set the "length" property of curried functions. * * @static * @memberOf _ @@ -9265,7 +9465,7 @@ function now() { * // `initialize` invokes `createApplication` once */ function once(func) { - return before(func, 2); + return before(2, func); } /** @@ -9276,7 +9476,7 @@ function now() { * The `_.partial.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * - * **Note:** This method does not set the `length` property of partially + * **Note:** This method does not set the "length" property of partially * applied functions. * * @static @@ -9309,7 +9509,7 @@ function now() { * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * - * **Note:** This method does not set the `length` property of partially + * **Note:** This method does not set the "length" property of partially * applied functions. * * @static @@ -9393,7 +9593,7 @@ function now() { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } - start = nativeMax(typeof start == 'undefined' ? (func.length - 1) : (+start || 0), 0); + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); return function() { var args = arguments, index = -1, @@ -9797,7 +9997,7 @@ function now() { if (value == null) { return true; } - var length = value.length; + var length = getLength(value); if (isLength(length) && (isArray(value) || isString(value) || isArguments(value) || (isObjectLike(value) && isFunction(value.splice)))) { return !length; @@ -9823,7 +10023,7 @@ function now() { * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparing values. + * @param {Function} [customizer] The function to customize value comparisons. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example @@ -9854,7 +10054,7 @@ function now() { return value === other; } var result = customizer ? customizer(value, other) : undefined; - return typeof result == 'undefined' ? baseIsEqual(value, other, customizer) : !!result; + return result === undefined ? baseIsEqual(value, other, customizer) : !!result; } /** @@ -9976,7 +10176,7 @@ function now() { * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize comparing values. + * @param {Function} [customizer] The function to customize value comparisons. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example @@ -10009,12 +10209,13 @@ function now() { return false; } customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 3); + object = toObject(object); if (!customizer && length == 1) { var key = props[0], value = source[key]; if (isStrictComparable(value)) { - return value === object[key] && (typeof value != 'undefined' || (key in toObject(object))); + return value === object[key] && (value !== undefined || (key in object)); } } var values = Array(length), @@ -10024,7 +10225,7 @@ function now() { value = values[length] = source[props[length]]; strictCompareFlags[length] = isStrictComparable(value); } - return baseIsMatch(toObject(object), props, values, strictCompareFlags, customizer); + return baseIsMatch(object, props, values, strictCompareFlags, customizer); } /** @@ -10079,9 +10280,9 @@ function now() { return false; } if (objToString.call(value) == funcTag) { - return reNative.test(fnToString.call(value)); + return reIsNative.test(fnToString.call(value)); } - return isObjectLike(value) && reHostCtor.test(value); + return isObjectLike(value) && reIsHostCtor.test(value); } /** @@ -10249,7 +10450,7 @@ function now() { * // => false */ function isUndefined(value) { - return typeof value == 'undefined'; + return value === undefined; } /** @@ -10268,7 +10469,7 @@ function now() { * // => [2, 3] */ function toArray(value) { - var length = value ? value.length : 0; + var length = value ? getLength(value) : 0; if (!isLength(length)) { return values(value); } @@ -10314,13 +10515,17 @@ function now() { * The `customizer` is bound to `thisArg` and invoked with five arguments: * (objectValue, sourceValue, key, object, source). * + * **Note:** This method mutates `object` and is based on + * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). + * + * * @static * @memberOf _ * @alias extend * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize assigning values. + * @param {Function} [customizer] The function to customize assigned values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {Object} Returns `object`. * @example @@ -10330,13 +10535,17 @@ function now() { * * // using a customizer callback * var defaults = _.partialRight(_.assign, function(value, other) { - * return typeof value == 'undefined' ? other : value; + * return _.isUndefined(value) ? other : value; * }); * * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); * // => { 'user': 'barney', 'age': 36 } */ - var assign = createAssigner(baseAssign); + var assign = createAssigner(function(object, source, customizer) { + return customizer + ? assignWith(object, source, customizer) + : baseAssign(object, source); + }); /** * Creates an object that inherits from the given `prototype` object. If a @@ -10377,7 +10586,7 @@ function now() { if (guard && isIterateeCall(prototype, properties, guard)) { properties = null; } - return properties ? baseCopy(properties, result, keys(properties)) : result; + return properties ? baseAssign(result, properties) : result; } /** @@ -10385,6 +10594,8 @@ function now() { * object for all destination properties that resolve to `undefined`. Once a * property is set, additional values of the same property are ignored. * + * **Note:** This method mutates `object`. + * * @static * @memberOf _ * @category Object @@ -10508,7 +10719,7 @@ function now() { /** * Iterates over own and inherited enumerable properties of an object invoking * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked - * with three arguments: (value, key, object). Iterator functions may exit + * with three arguments: (value, key, object). Iteratee functions may exit * iteration early by explicitly returning `false`. * * @static @@ -10564,7 +10775,7 @@ function now() { /** * Iterates over own enumerable properties of an object invoking `iteratee` * for each property. The `iteratee` is bound to `thisArg` and invoked with - * three arguments: (value, key, object). Iterator functions may exit iteration + * three arguments: (value, key, object). Iteratee functions may exit iteration * early by explicitly returning `false`. * * @static @@ -10637,24 +10848,68 @@ function now() { } /** - * Checks if `key` exists as a direct property of `object` instead of an - * inherited property. + * Gets the property value of `path` on `object`. If the resolved value is + * `undefined` the `defaultValue` is used in its place. * * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. - * @param {string} key The key to check. - * @returns {boolean} Returns `true` if `key` is a direct property, else `false`. + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. * @example * - * var object = { 'a': 1, 'b': 2, 'c': 3 }; + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * - * _.has(object, 'b'); + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, toPath(path), path + ''); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. + * @example + * + * var object = { 'a': { 'b': { 'c': 3 } } }; + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b.c'); + * // => true + * + * _.has(object, ['a', 'b', 'c']); * // => true */ - function has(object, key) { - return object ? hasOwnProperty.call(object, key) : false; + function has(object, path) { + if (object == null) { + return false; + } + var result = hasOwnProperty.call(object, path); + if (!result && !isKey(path)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + result = object != null && hasOwnProperty.call(object, path); + } + return result; } /** @@ -10717,7 +10972,7 @@ function now() { * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * @@ -10740,7 +10995,7 @@ function now() { length = object.length; } if ((typeof Ctor == 'function' && Ctor.prototype === object) || - (typeof object != 'function' && (length && isLength(length)))) { + (typeof object != 'function' && isLength(length))) { return shimKeys(object); } return isObject(object) ? nativeKeys(object) : []; @@ -10754,7 +11009,7 @@ function now() { * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * @@ -10862,7 +11117,7 @@ function now() { * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize merging properties. + * @param {Function} [customizer] The function to customize assigned values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {Object} Returns `object`. * @example @@ -10947,7 +11202,7 @@ function now() { * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the new array of key-value pairs. * @example * @@ -11003,41 +11258,93 @@ function now() { }); /** - * Resolves the value of property `key` on `object`. If the value of `key` is - * a function it is invoked with the `this` binding of `object` and its result - * is returned, else the property value is returned. If the property value is - * `undefined` the `defaultValue` is used in its place. + * This method is like `_.get` except that if the resolved value is a function + * it is invoked with the `this` binding of its parent object and its result + * is returned. * * @static * @memberOf _ * @category Object * @param {Object} object The object to query. - * @param {string} key The key of the property to resolve. - * @param {*} [defaultValue] The value returned if the property value - * resolves to `undefined`. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. * @returns {*} Returns the resolved value. * @example * - * var object = { 'user': 'fred', 'age': _.constant(40) }; + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; * - * _.result(object, 'user'); - * // => 'fred' + * _.result(object, 'a[0].b.c1'); + * // => 3 * - * _.result(object, 'age'); - * // => 40 + * _.result(object, 'a[0].b.c2'); + * // => 4 * - * _.result(object, 'status', 'busy'); - * // => 'busy' + * _.result(object, 'a.b.c', 'default'); + * // => 'default' * - * _.result(object, 'status', _.constant('busy')); - * // => 'busy' + * _.result(object, 'a.b.c', _.constant('default')); + * // => 'default' */ - function result(object, key, defaultValue) { - var value = object == null ? undefined : object[key]; - if (typeof value == 'undefined') { - value = defaultValue; + function result(object, path, defaultValue) { + var result = object == null ? undefined : object[path]; + if (result === undefined) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + result = object == null ? undefined : object[last(path)]; + } + result = result === undefined ? defaultValue : result; } - return isFunction(value) ? value.call(object) : value; + return isFunction(result) ? result.call(object) : result; + } + + /** + * Sets the property value of `path` on `object`. If a portion of `path` + * does not exist it is created. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to augment. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, 'x[0].y.z', 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + if (object == null) { + return object; + } + var pathKey = (path + ''); + path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path); + + var index = -1, + length = path.length, + endIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = path[index]; + if (isObject(nested)) { + if (index == endIndex) { + nested[key] = value; + } else if (nested[key] == null) { + nested[key] = isIndex(path[index + 1]) ? [] : {}; + } + } + nested = nested[key]; + } + return object; } /** @@ -11045,7 +11352,7 @@ function now() { * `accumulator` object which is the result of running each of its own enumerable * properties through `iteratee`, with each invocation potentially mutating * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked - * with four arguments: (accumulator, value, key, object). Iterator functions + * with four arguments: (accumulator, value, key, object). Iteratee functions * may exit iteration early by explicitly returning `false`. * * @static @@ -11188,7 +11495,7 @@ function now() { } else { end = +end || 0; } - return value >= start && value < end; + return value >= nativeMin(start, end) && value < nativeMax(start, end); } /** @@ -11313,7 +11620,7 @@ function now() { */ function deburr(string) { string = baseToString(string); - return string && string.replace(reLatin1, deburrLetter).replace(reComboMarks, ''); + return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, ''); } /** @@ -11342,7 +11649,7 @@ function now() { target = (target + ''); var length = string.length; - position = typeof position == 'undefined' + position = position === undefined ? length : nativeMin(position < 0 ? 0 : (+position || 0), length); @@ -11364,9 +11671,10 @@ function now() { * (under "semi-related fun fact") for more details. * * Backticks are escaped because in Internet Explorer < 9, they can break out - * of attribute values or HTML comments. See [#102](https://html5sec.org/#102), - * [#108](https://html5sec.org/#108), and [#133](https://html5sec.org/#133) of - * the [HTML5 Security Cheatsheet](https://html5sec.org/) for more details. + * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), + * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and + * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) + * for more details. * * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) * to reduce XSS vectors. @@ -11560,7 +11868,7 @@ function now() { radix = +radix; } string = trim(string); - return nativeParseInt(string, radix || (reHexPrefix.test(string) ? 16 : 10)); + return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10)); }; } @@ -11785,9 +12093,9 @@ function now() { options = otherOptions = null; } string = baseToString(string); - options = baseAssign(baseAssign({}, otherOptions || options), settings, assignOwnDefaults); + options = assignWith(baseAssign({}, otherOptions || options), settings, assignOwnDefaults); - var imports = baseAssign(baseAssign({}, options.imports), settings.imports, assignOwnDefaults), + var imports = assignWith(baseAssign({}, options.imports), settings.imports, assignOwnDefaults), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys); @@ -12191,9 +12499,7 @@ function now() { if (guard && isIterateeCall(func, thisArg, guard)) { thisArg = null; } - return isObjectLike(func) - ? matches(func) - : baseCallback(func, thisArg); + return baseCallback(func, thisArg); } /** @@ -12267,7 +12573,7 @@ function now() { } /** - * Creates a function which compares the property value of `key` on a given + * Creates a function which compares the property value of `path` on a given * object to `value`. * * **Note:** This method supports comparing arrays, booleans, `Date` objects, @@ -12277,7 +12583,7 @@ function now() { * @static * @memberOf _ * @category Utility - * @param {string} key The key of the property to get. + * @param {Array|string} path The path of the property to get. * @param {*} value The value to compare. * @returns {Function} Returns the new function. * @example @@ -12290,22 +12596,75 @@ function now() { * _.find(users, _.matchesProperty('user', 'fred')); * // => { 'user': 'fred' } */ - function matchesProperty(key, value) { - return baseMatchesProperty(key + '', baseClone(value, true)); + function matchesProperty(path, value) { + return baseMatchesProperty(path, baseClone(value, true)); } + /** + * Creates a function which invokes the method at `path` on a given object. + * + * @static + * @memberOf _ + * @category Utility + * @param {Array|string} path The path of the method to invoke. + * @returns {Function} Returns the new function. + * @example + * + * var objects = [ + * { 'a': { 'b': { 'c': _.constant(2) } } }, + * { 'a': { 'b': { 'c': _.constant(1) } } } + * ]; + * + * _.map(objects, _.method('a.b.c')); + * // => [2, 1] + * + * _.invoke(_.sortBy(objects, _.method(['a', 'b', 'c'])), 'a.b.c'); + * // => [1, 2] + */ + var method = restParam(function(path, args) { + return function(object) { + return invokePath(object, path, args); + } + }); + + /** + * The opposite of `_.method`; this method creates a function which invokes + * the method at a given path on `object`. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} object The object to query. + * @returns {Function} Returns the new function. + * @example + * + * var array = _.times(3, _.constant), + * object = { 'a': array, 'b': array, 'c': array }; + * + * _.map(['a[2]', 'c[0]'], _.methodOf(object)); + * // => [2, 0] + * + * _.map([['a', '2'], ['c', '0']], _.methodOf(object)); + * // => [2, 0] + */ + var methodOf = restParam(function(object, args) { + return function(path) { + return invokePath(object, path, args); + }; + }); + /** * Adds all own enumerable function properties of a source object to the * destination object. If `object` is a function then methods are added to * its prototype as well. * - * **Note:** Use `_.runInContext` to create a pristine `lodash` function - * for mixins to avoid conflicts caused by modifying the original. + * **Note:** Use `_.runInContext` to create a pristine `lodash` function to + * avoid conflicts caused by modifying the original. * * @static * @memberOf _ * @category Utility - * @param {Function|Object} [object=this] object The destination object. + * @param {Function|Object} [object=lodash] The destination object. * @param {Object} source The object of functions to add. * @param {Object} [options] The options object. * @param {boolean} [options.chain=true] Specify whether the functions added @@ -12422,61 +12781,61 @@ function now() { } /** - * Creates a function which returns the property value of `key` on a given object. + * Creates a function which returns the property value at `path` on a + * given object. * * @static * @memberOf _ * @category Utility - * @param {string} key The key of the property to get. + * @param {Array|string} path The path of the property to get. * @returns {Function} Returns the new function. * @example * - * var users = [ - * { 'user': 'fred' }, - * { 'user': 'barney' } + * var objects = [ + * { 'a': { 'b': { 'c': 2 } } }, + * { 'a': { 'b': { 'c': 1 } } } * ]; * - * var getName = _.property('user'); + * _.map(objects, _.property('a.b.c')); + * // => [2, 1] * - * _.map(users, getName); - * // => ['fred', 'barney'] - * - * _.pluck(_.sortBy(users, getName), 'user'); - * // => ['barney', 'fred'] + * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c'); + * // => [1, 2] */ - function property(key) { - return baseProperty(key + ''); + function property(path) { + return isKey(path) ? baseProperty(path) : basePropertyDeep(path); } /** * The opposite of `_.property`; this method creates a function which returns - * the property value of a given key on `object`. + * the property value at a given path on `object`. * * @static * @memberOf _ * @category Utility - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Function} Returns the new function. * @example * - * var object = { 'a': 3, 'b': 1, 'c': 2 }; + * var array = [0, 1, 2], + * object = { 'a': array, 'b': array, 'c': array }; * - * _.map(['a', 'c'], _.propertyOf(object)); - * // => [3, 2] + * _.map(['a[2]', 'c[0]'], _.propertyOf(object)); + * // => [2, 0] * - * _.sortBy(['a', 'b', 'c'], _.propertyOf(object)); - * // => ['b', 'c', 'a'] + * _.map([['a', '2'], ['c', '0']], _.propertyOf(object)); + * // => [2, 0] */ function propertyOf(object) { - return function(key) { - return object == null ? undefined : object[key]; + return function(path) { + return baseGet(object, toPath(path), path + ''); }; } /** * Creates an array of numbers (positive and/or negative) progressing from * `start` up to, but not including, `end`. If `end` is not specified it is - * set to `start` with `start` then set to `0`. If `start` is less than `end` + * set to `start` with `start` then set to `0`. If `end` is less than `start` * a zero-length range is created unless a negative `step` is specified. * * @static @@ -12552,7 +12911,7 @@ function now() { * _.times(3, function(n) { * mage.castSpell(n); * }); - * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2` respectively + * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2` * * _.times(3, function(n) { * this.cast(n); @@ -12560,7 +12919,7 @@ function now() { * // => also invokes `mage.castSpell(n)` three times */ function times(n, iteratee, thisArg) { - n = +n; + n = floor(n); // Exit early to avoid a JSC JIT bug in Safari 8 // where `Array(0)` is treated as `Array(1)`. @@ -12619,7 +12978,7 @@ function now() { * // => 10 */ function add(augend, addend) { - return augend + addend; + return (+augend || 0) + (+addend || 0); } /** @@ -12845,6 +13204,8 @@ function now() { lodash.matchesProperty = matchesProperty; lodash.memoize = memoize; lodash.merge = merge; + lodash.method = method; + lodash.methodOf = methodOf; lodash.mixin = mixin; lodash.negate = negate; lodash.omit = omit; @@ -12865,6 +13226,7 @@ function now() { lodash.remove = remove; lodash.rest = rest; lodash.restParam = restParam; + lodash.set = set; lodash.shuffle = shuffle; lodash.slice = slice; lodash.sortBy = sortBy; @@ -12933,6 +13295,7 @@ function now() { lodash.findLastKey = findLastKey; lodash.findWhere = findWhere; lodash.first = first; + lodash.get = get; lodash.has = has; lodash.identity = identity; lodash.includes = includes; @@ -13121,7 +13484,7 @@ function now() { // Add `LazyWrapper` methods for `_.pluck` and `_.where`. arrayEach(['pluck', 'where'], function(methodName, index) { var operationName = index ? 'filter' : 'map', - createCallback = index ? baseMatches : baseProperty; + createCallback = index ? baseMatches : property; LazyWrapper.prototype[methodName] = function(value) { return this[operationName](createCallback(value)); @@ -13143,7 +13506,7 @@ function now() { start = start == null ? 0 : (+start || 0); var result = start < 0 ? this.takeRight(-start) : this.drop(start); - if (typeof end != 'undefined') { + if (end !== undefined) { end = (+end || 0); result = end < 0 ? result.dropRight(-end) : result.take(end - start); } @@ -13174,7 +13537,7 @@ function now() { useLazy = isLazy || isArray(value); if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) { - // avoid lazy use if the iteratee has a `length` other than `1` + // avoid lazy use if the iteratee has a "length" value other than `1` isLazy = useLazy = false; } var onlyLazy = isLazy && !isHybrid; @@ -13636,7 +13999,7 @@ Lexer.prototype = { } // 2-character punctuators: <= >= == != ++ -- << >> && || - // += -= *= %= &= |= ^= (but not /=, see below) + // += -= *= %= &= |= ^= /= if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { return { type: Token.Punctuator, @@ -13644,7 +14007,7 @@ Lexer.prototype = { }; } - if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { + if ("<>=!+-*%&|^/".indexOf(ch1) >= 0) { if (ch2 === "=") { return { type: Token.Punctuator, @@ -13658,22 +14021,6 @@ Lexer.prototype = { }; } - // Special case: /=. - - if (ch1 === "/") { - if (ch2 === "=") { - return { - type: Token.Punctuator, - value: "/=" - }; - } - - return { - type: Token.Punctuator, - value: "/" - }; - } - return null; }, @@ -13693,6 +14040,7 @@ Lexer.prototype = { var rest = this.input.substr(2); var startLine = this.line; var startChar = this.char; + var self = this; // Create a comment token object and make sure it // has all the data JSHint needs to work with special @@ -13711,6 +14059,11 @@ Lexer.prototype = { body = body.replace(/\n/g, " "); + if (label === "/*" && reg.fallsThrough.test(body)) { + isSpecial = true; + commentType = "falls through"; + } + special.forEach(function(str) { if (isSpecial) { return; @@ -13747,6 +14100,26 @@ Lexer.prototype = { commentType = "globals"; break; default: + var options = body.split(":").map(function(v) { + return v.replace(/^\s+/, "").replace(/\s+$/, ""); + }); + + if (options.length === 2) { + switch (options[0]) { + case "ignore": + switch (options[1]) { + case "start": + self.ignoringLinterErrors = true; + isSpecial = false; + break; + case "end": + self.ignoringLinterErrors = false; + isSpecial = false; + break; + } + } + } + commentType = str; } }); @@ -14061,12 +14434,12 @@ Lexer.prototype = { isAllowedDigit = isOctalDigit; base = 8; - if (!state.option.esnext) { + if (!state.inES6(true)) { this.trigger("warning", { code: "W119", line: this.line, character: this.char, - data: [ "Octal integer literal" ] + data: [ "Octal integer literal", "6" ] }); } @@ -14079,12 +14452,12 @@ Lexer.prototype = { isAllowedDigit = isBinaryDigit; base = 2; - if (!state.option.esnext) { + if (!state.inES6(true)) { this.trigger("warning", { code: "W119", line: this.line, character: this.char, - data: [ "Binary integer literal" ] + data: [ "Binary integer literal", "6" ] }); } @@ -14326,10 +14699,15 @@ Lexer.prototype = { var startChar = this.char; var depth = this.templateStarts.length; - if (!state.option.esnext) { - // Only lex template strings in ESNext mode. - return null; - } else if (this.peek() === "`") { + if (this.peek() === "`") { + if (!state.inES6(true)) { + this.trigger("warning", { + code: "W119", + line: this.line, + character: this.char, + data: ["template literal syntax", "6"] + }); + } // Template must start with a backtick. tokenType = Token.TemplateHead; this.templateStarts.push({ line: this.line, char: this.char }); @@ -14728,14 +15106,9 @@ Lexer.prototype = { this.from = this.char; // Move to the next non-space character. - var start; - if (/\s/.test(this.peek())) { - start = this.char; - - while (/\s/.test(this.peek())) { - this.from += 1; - this.skip(); - } + while (/\s/.test(this.peek())) { + this.from += 1; + this.skip(); } // Methods that work with multi-line structures and move the @@ -14800,7 +15173,7 @@ Lexer.prototype = { // If we are ignoring linter errors, replace the input with empty string // if it doesn't already at least start or end a multi-line comment - if (state.ignoreLinterErrors === true) { + if (this.ignoringLinterErrors === true) { if (!startsWith("/*", "//") && !(this.inComment && endsWith("*/"))) { this.input = ""; } @@ -14821,7 +15194,8 @@ Lexer.prototype = { // If there is a limit on line length, warn when lines get too // long. - if (state.option.maxlen && state.option.maxlen < this.input.length) { + if (!this.ignoringLinterErrors && state.option.maxlen && + state.option.maxlen < this.input.length) { var inComment = this.inComment || startsWith.call(inputTrimmed, "//") || startsWith.call(inputTrimmed, "/*"); @@ -14910,7 +15284,8 @@ Lexer.prototype = { } if (type === "(identifier)") { - if (value === "return" || value === "case" || value === "typeof") { + if (value === "return" || value === "case" || + value === "typeof" || value === "instanceof") { this.prereg = true; } @@ -14924,6 +15299,10 @@ Lexer.prototype = { } } + if (type === "(template)" || type === "(template middle)") { + this.prereg = true; + } + if (!obj) { obj = Object.create(state.syntax[type]); } @@ -15051,14 +15430,14 @@ Lexer.prototype = { return create("(no subst template)", token.value, null, token); case Token.Identifier: - this.trigger("Identifier", { + this.triggerAsync("Identifier", { line: this.line, char: this.char, - from: this.form, + from: this.from, name: token.value, raw_name: token.text, isProperty: state.tokens.curr.id === "." - }); + }, checks, function() { return true; }); /* falls through */ case Token.Keyword: @@ -15136,7 +15515,7 @@ Lexer.prototype = { exports.Lexer = Lexer; exports.Context = Context; -},{"../data/ascii-identifier-data.js":1,"../data/non-ascii-identifier-part-only.js":2,"../data/non-ascii-identifier-start.js":3,"./reg.js":17,"./state.js":18,"events":5,"lodash":12}],14:[function(require,module,exports){ +},{"../data/ascii-identifier-data.js":1,"../data/non-ascii-identifier-part-only.js":2,"../data/non-ascii-identifier-start.js":3,"./reg.js":17,"./state.js":19,"events":5,"lodash":12}],14:[function(require,module,exports){ "use strict"; var _ = require("lodash"); @@ -15159,7 +15538,7 @@ var errors = { E010: "'with' is not allowed in strict mode.", // Constants - E011: "const '{a}' has already been declared.", + E011: "'{a}' has already been declared.", E012: "const '{a}' is initialized to 'undefined'.", E013: "Attempting to override '{a}' which is a constant.", @@ -15201,15 +15580,19 @@ var errors = { E044: null, E045: "Invalid for each loop.", E046: "A yield statement shall be within a generator function (with syntax: `function*`)", - E047: null, // Vacant + E047: null, E048: "{a} declaration not directly within block.", E049: "A {a} cannot be named '{b}'.", E050: "Mozilla requires the yield expression to be parenthesized here.", - E051: "Regular parameters cannot come after default parameters.", + E051: null, E052: "Unclosed template literal.", E053: "Export declaration must be in global scope.", E054: "Class properties must be methods. Expected '(' but instead saw '{a}'.", - E055: "The '{a}' option cannot be set after any executable code." + E055: "The '{a}' option cannot be set after any executable code.", + E056: "'{a}' was used before it was declared, which is illegal for '{b}' variables.", + E057: "Invalid meta property: '{a}.{b}'.", + E058: "Missing semicolon.", + E059: "Incompatible values for the '{a}' and '{b}' linting options." }; var warnings = { @@ -15233,7 +15616,8 @@ var warnings = { W018: "Confusing use of '{a}'.", W019: "Use the isNaN function to compare with NaN.", W020: "Read only.", - W021: "'{a}' is a function.", + W021: "Reassignment of '{a}', which is is a {b}. " + + "Use 'var' or 'let' to declare bindings that may change.", W022: "Do not assign to the exception parameter.", W023: "Expected an identifier in an assignment and instead saw a function invocation.", W024: "Expected an identifier and instead saw '{a}' (a reserved word).", @@ -15307,7 +15691,7 @@ var warnings = { W089: "The body of a for in should be wrapped in an if statement to filter " + "unwanted properties from the prototype.", W090: "'{a}' is not a statement label.", - W091: "'{a}' is out of scope.", + W091: null, W093: "Did you mean to return a conditional instead of an assignment?", W094: "Unexpected comma.", W095: "Expected a string and instead saw {a}.", @@ -15319,7 +15703,7 @@ var warnings = { W101: "Line is too long.", W102: null, W103: "The '{a}' property is deprecated.", - W104: "'{a}' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).", + W104: "'{a}' is available in ES{b} (use 'esversion: {b}') or Mozilla JS extensions (use moz).", W105: "Unexpected {a} in '{b}'.", W106: "Identifier '{a}' is not in camel case.", W107: "Script URL.", @@ -15333,13 +15717,13 @@ var warnings = { W116: "Expected '{a}' and instead saw '{b}'.", W117: "'{a}' is not defined.", W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", - W119: "'{a}' is only available in ES6 (use esnext option).", + W119: "'{a}' is only available in ES{b} (use 'esversion: {b}').", W120: "You might be leaking a variable ({a}) here.", W121: "Extending prototype of native object: '{a}'.", W122: "Invalid typeof value '{a}'", W123: "'{a}' is already defined in outer scope.", W124: "A generator function shall contain a yield statement.", - W125: "This line contains non-breaking spaces: http://jshint.com/doc/options/#nonbsp", + W125: "This line contains non-breaking spaces: http://jshint.com/docs/options/#nonbsp", W126: "Unnecessary grouping operator.", W127: "Unexpected use of a comma operator.", W128: "Empty array elements require elision=true.", @@ -15349,7 +15733,11 @@ var warnings = { W131: "Invalid parameter after rest parameter.", W132: "`var` declarations are forbidden. Use `let` or `const` instead.", W133: "Invalid for-{a} loop left-hand-side: {b}.", - W134: "The '{a}' option is only available when linting ECMAScript {b} code." + W134: "The '{a}' option is only available when linting ECMAScript {b} code.", + W135: "{a} may not be supported by non-browser environments.", + W136: "'{a}' must be in function scope.", + W137: "Empty destructuring.", + W138: "Regular parameters should not come after default parameters." }; var info = { @@ -15544,6 +15932,8 @@ exports.bool = { * specification. Use this option if you need your program to be executable * in older browsers—such as Internet Explorer 6/7/8/9—and other legacy * JavaScript environments. + * + * @deprecated Use `esversion: 3` instead. */ es3 : true, @@ -15551,6 +15941,8 @@ exports.bool = { * This option enables syntax first defined in [the ECMAScript 5.1 * specification](http://es5.github.io/). This includes allowing reserved * keywords as object properties. + * + * @deprecated Use `esversion: 5` instead. */ es5 : true, @@ -15711,21 +16103,6 @@ exports.bool = { */ singleGroups: false, - /** - * This option requires all functions to run in ECMAScript 5's strict mode. - * [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) - * is a way to opt in to a restricted variant of JavaScript. Strict mode - * eliminates some JavaScript pitfalls that didn't cause errors by changing - * them to produce errors. It also fixes mistakes that made it difficult - * for the JavaScript engines to perform certain optimizations. - * - * *Note:* This option enables strict mode for function scope only. It - * *prohibits* the global scoped strict mode because it might break - * third-party widgets on your page. If you really want to use global - * strict mode, see the *globalstrict* option. - */ - strict : true, - /** * When set to true, the use of VariableStatements are forbidden. * For example: @@ -15828,6 +16205,8 @@ exports.bool = { * recommended. * * For more info about strict mode see the `strict` option. + * + * @deprecated Use `strict: "global"`. */ globalstrict: true, @@ -16003,13 +16382,14 @@ exports.bool = { /** * This option tells JSHint that your code uses ECMAScript 6 specific - * syntax. Note that these features are not finalized yet and not all - * browsers implement them. + * syntax. Note that not all browsers implement these features. * * More info: * - * * [Draft Specification for ES.next (ECMA-262 Ed. - * 6)](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts) + * * [Specification for ECMAScript + * 6](http://www.ecma-international.org/ecma-262/6.0/index.html) + * + * @deprecated Use `esversion: 6` instead. */ esnext : true, @@ -16210,7 +16590,18 @@ exports.val = { */ maxerr : false, - predef : false, // predef is deprecated and being replaced by globals + /** + * This option allows you to control which variables JSHint considers to be + * implicitly defined in the environment. Configure it with an array of + * string values. Prefixing a variable name with a hyphen (-) character will + * remove that name from the collection of predefined variables. + * + * JSHint will consider variables declared in this way to be read-only. + * + * This option cannot be specified in-line; it may only be used via the + * JavaScript API or from an external configuration file. + */ + predef : false, /** * This option can be used to specify a white list of global variables that @@ -16225,6 +16616,9 @@ exports.val = { * See also the "environment" options: a set of options to be used as short * hand for enabling global variables defined in common JavaScript * environments. + * + * To configure `globals` within an individual file, see [Inline + * Configuration](http://jshint.com/docs/#inline-configuration). */ globals : false, @@ -16324,6 +16718,25 @@ exports.val = { */ shadow : false, + /** + * This option requires the code to run in ECMAScript 5's strict mode. + * [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) + * is a way to opt in to a restricted variant of JavaScript. Strict mode + * eliminates some JavaScript pitfalls that didn't cause errors by changing + * them to produce errors. It also fixes mistakes that made it difficult + * for the JavaScript engines to perform certain optimizations. + * + * - "global" - there must be a `"use strict";` directive at global level + * - "implied" - lint the code as if there is the `"use strict";` directive + * - false - disable warnings about strict mode + * - true - there must be a `"use strict";` directive at function level; + * this is preferable for scripts intended to be loaded in web + * browsers directly because enabling strict mode globally + * could adversely effect other scripts running on the same + * page + */ + strict : true, + /** * This option warns when you define and never use your variables. It is very * useful for general code cleanup, especially when used in addition to @@ -16374,8 +16787,24 @@ exports.val = { // end - stop ignoring lines, starting on the next line // line - ignore warnings / errors for just a single line // (this option does not bypass the lexer) - ignoreDelimiters: false // array of start/end delimiters used to ignore - // certain chunks from code + + ignoreDelimiters: false, // array of start/end delimiters used to ignore + // certain chunks from code + + /** + * This option is used to specify the ECMAScript version to which the code + * must adhere. It can assume one of the following values: + * - `3` - If you need your program to be executable + * in older browsers—such as Internet Explorer 6/7/8/9—and other legacy + * JavaScript environments + * - `5` - To enable syntax first defined in [the ECMAScript 5.1 + * specification](http://www.ecma-international.org/ecma-262/5.1/index.html). + * This includes allowing reserved keywords as object properties. + * - `6` - To tell JSHint that your code uses [ECMAScript + * 6](http://www.ecma-international.org/ecma-262/6.0/index.html) specific + * syntax. Note that not all browsers implement them. + */ + esversion: 5 }; // These are JSHint boolean options which are shared with JSLint @@ -16458,7 +16887,7 @@ exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i; // Catches /* falls through */ comments (ft) -exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; +exports.fallsThrough = /^\s*falls?\sthrough\s*$/; // very conservative rule (eg: only one space between the start of the comment and the first character) // to relax the maxlen option @@ -16466,6 +16895,865 @@ exports.maxlenException = /^(?:(?:\/\/|\/\*|\*) ?)?[^ ]+$/; },{}],18:[function(require,module,exports){ "use strict"; + +var _ = require("lodash"); +var events = require("events"); + +// Used to denote membership in lookup tables (a primitive value such as `true` +// would be silently rejected for the property name "__proto__" in some +// environments) +var marker = {}; + +/** + * Creates a scope manager that handles variables and labels, storing usages + * and resolving when variables are used and undefined + */ +var scopeManager = function(state, predefined, exported, declared) { + + var _current; + var _scopeStack = []; + + function _newScope(type) { + _current = { + "(labels)": Object.create(null), + "(usages)": Object.create(null), + "(breakLabels)": Object.create(null), + "(parent)": _current, + "(type)": type, + "(params)": (type === "functionparams" || type === "catchparams") ? [] : null + }; + _scopeStack.push(_current); + } + + _newScope("global"); + _current["(predefined)"] = predefined; + + var _currentFunctBody = _current; // this is the block after the params = function + + var usedPredefinedAndGlobals = Object.create(null); + var impliedGlobals = Object.create(null); + var unuseds = []; + var emitter = new events.EventEmitter(); + + function warning(code, token) { + emitter.emit("warning", { + code: code, + token: token, + data: _.slice(arguments, 2) + }); + } + + function error(code, token) { + emitter.emit("warning", { + code: code, + token: token, + data: _.slice(arguments, 2) + }); + } + + function _setupUsages(labelName) { + if (!_current["(usages)"][labelName]) { + _current["(usages)"][labelName] = { + "(modified)": [], + "(reassigned)": [], + "(tokens)": [] + }; + } + } + + var _getUnusedOption = function(unused_opt) { + if (unused_opt === undefined) { + unused_opt = state.option.unused; + } + + if (unused_opt === true) { + unused_opt = "last-param"; + } + + return unused_opt; + }; + + var _warnUnused = function(name, tkn, type, unused_opt) { + var line = tkn.line; + var chr = tkn.from; + var raw_name = tkn.raw_text || name; + + unused_opt = _getUnusedOption(unused_opt); + + var warnable_types = { + "vars": ["var"], + "last-param": ["var", "param"], + "strict": ["var", "param", "last-param"] + }; + + if (unused_opt) { + if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { + warning("W098", { line: line, from: chr }, raw_name); + } + } + + // inconsistent - see gh-1894 + if (unused_opt || type === "var") { + unuseds.push({ + name: name, + line: line, + character: chr + }); + } + }; + + /** + * Checks the current scope for unused identifiers + */ + function _checkForUnused() { + // function params are handled specially + // assume that parameters are the only thing declared in the param scope + if (_current["(type)"] === "functionparams") { + _checkParams(); + return; + } + var curentLabels = _current["(labels)"]; + for (var labelName in curentLabels) { + if (curentLabels[labelName]) { + if (curentLabels[labelName]["(type)"] !== "exception" && + curentLabels[labelName]["(unused)"]) { + _warnUnused(labelName, curentLabels[labelName]["(token)"], "var"); + } + } + } + } + + /** + * Checks the current scope for unused parameters + * Must be called in a function parameter scope + */ + function _checkParams() { + var params = _current["(params)"]; + + if (!params) { + return; + } + + var param = params.pop(); + var unused_opt; + + while (param) { + var label = _current["(labels)"][param]; + + unused_opt = _getUnusedOption(state.funct["(unusedOption)"]); + + // 'undefined' is a special case for (function(window, undefined) { ... })(); + // patterns. + if (param === "undefined") + return; + + if (label["(unused)"]) { + _warnUnused(param, label["(token)"], "param", state.funct["(unusedOption)"]); + } else if (unused_opt === "last-param") { + return; + } + + param = params.pop(); + } + } + + /** + * Finds the relevant label's scope, searching from nearest outwards + * @returns {Object} the scope the label was found in + */ + function _getLabel(labelName) { + for (var i = _scopeStack.length - 1 ; i >= 0; --i) { + var scopeLabels = _scopeStack[i]["(labels)"]; + if (scopeLabels[labelName]) { + return scopeLabels; + } + } + } + + function usedSoFarInCurrentFunction(labelName) { + // used so far in this whole function and any sub functions + for (var i = _scopeStack.length - 1; i >= 0; i--) { + var current = _scopeStack[i]; + if (current["(usages)"][labelName]) { + return current["(usages)"][labelName]; + } + if (current === _currentFunctBody) { + break; + } + } + return false; + } + + function _checkOuterShadow(labelName, token) { + + // only check if shadow is outer + if (state.option.shadow !== "outer") { + return; + } + + var isGlobal = _currentFunctBody["(type)"] === "global", + isNewFunction = _current["(type)"] === "functionparams"; + + var outsideCurrentFunction = !isGlobal; + for (var i = 0; i < _scopeStack.length; i++) { + var stackItem = _scopeStack[i]; + + if (!isNewFunction && _scopeStack[i + 1] === _currentFunctBody) { + outsideCurrentFunction = false; + } + if (outsideCurrentFunction && stackItem["(labels)"][labelName]) { + warning("W123", token, labelName); + } + if (stackItem["(breakLabels)"][labelName]) { + warning("W123", token, labelName); + } + } + } + + function _latedefWarning(type, labelName, token) { + if (state.option.latedef) { + // if either latedef is strict and this is a function + // or this is not a function + if ((state.option.latedef === true && type === "function") || + type !== "function") { + warning("W003", token, labelName); + } + } + } + + var scopeManagerInst = { + + on: function(names, listener) { + names.split(" ").forEach(function(name) { + emitter.on(name, listener); + }); + }, + + isPredefined: function(labelName) { + return !this.has(labelName) && _.has(_scopeStack[0]["(predefined)"], labelName); + }, + + /** + * Tell the manager we are entering a new block of code + * @param {string} [type] - The type of the block. Valid values are + * "functionparams", "catchparams" and + * "functionouter" + */ + stack: function(type) { + var previousScope = _current; + _newScope(type); + + if (!type && previousScope["(type)"] === "functionparams") { + + _current["(isFuncBody)"] = true; + _current["(context)"] = _currentFunctBody; + _currentFunctBody = _current; + } + }, + + unstack: function() { + // jshint proto: true + var subScope = _scopeStack.length > 1 ? _scopeStack[_scopeStack.length - 2] : null; + var isUnstackingFunctionBody = _current === _currentFunctBody, + isUnstackingFunctionParams = _current["(type)"] === "functionparams", + isUnstackingFunctionOuter = _current["(type)"] === "functionouter"; + + var i, j; + var currentUsages = _current["(usages)"]; + var currentLabels = _current["(labels)"]; + var usedLabelNameList = Object.keys(currentUsages); + + if (currentUsages.__proto__ && usedLabelNameList.indexOf("__proto__") === -1) { + usedLabelNameList.push("__proto__"); + } + + for (i = 0; i < usedLabelNameList.length; i++) { + var usedLabelName = usedLabelNameList[i]; + + var usage = currentUsages[usedLabelName]; + var usedLabel = currentLabels[usedLabelName]; + if (usedLabel) { + var usedLabelType = usedLabel["(type)"]; + + if (usedLabel["(useOutsideOfScope)"] && !state.option.funcscope) { + var usedTokens = usage["(tokens)"]; + if (usedTokens) { + for (j = 0; j < usedTokens.length; j++) { + // Keep the consistency of https://github.com/jshint/jshint/issues/2409 + if (usedLabel["(function)"] === usedTokens[j]["(function)"]) { + error("W038", usedTokens[j], usedLabelName); + } + } + } + } + + // mark the label used + _current["(labels)"][usedLabelName]["(unused)"] = false; + + // check for modifying a const + if (usedLabelType === "const" && usage["(modified)"]) { + for (j = 0; j < usage["(modified)"].length; j++) { + error("E013", usage["(modified)"][j], usedLabelName); + } + } + + // check for re-assigning a function declaration + if ((usedLabelType === "function" || usedLabelType === "class") && + usage["(reassigned)"]) { + for (j = 0; j < usage["(reassigned)"].length; j++) { + if (!usage["(reassigned)"][j].ignoreW021) { + warning("W021", usage["(reassigned)"][j], usedLabelName, usedLabelType); + } + } + } + continue; + } + + if (isUnstackingFunctionOuter) { + state.funct["(isCapturing)"] = true; + } + + if (subScope) { + // not exiting the global scope, so copy the usage down in case its an out of scope usage + if (!subScope["(usages)"][usedLabelName]) { + subScope["(usages)"][usedLabelName] = usage; + if (isUnstackingFunctionBody) { + subScope["(usages)"][usedLabelName]["(onlyUsedSubFunction)"] = true; + } + } else { + var subScopeUsage = subScope["(usages)"][usedLabelName]; + subScopeUsage["(modified)"] = subScopeUsage["(modified)"].concat(usage["(modified)"]); + subScopeUsage["(tokens)"] = subScopeUsage["(tokens)"].concat(usage["(tokens)"]); + subScopeUsage["(reassigned)"] = + subScopeUsage["(reassigned)"].concat(usage["(reassigned)"]); + } + } else { + // this is exiting global scope, so we finalise everything here - we are at the end of the file + if (typeof _current["(predefined)"][usedLabelName] === "boolean") { + + // remove the declared token, so we know it is used + delete declared[usedLabelName]; + + // note it as used so it can be reported + usedPredefinedAndGlobals[usedLabelName] = marker; + + // check for re-assigning a read-only (set to false) predefined + if (_current["(predefined)"][usedLabelName] === false && usage["(reassigned)"]) { + for (j = 0; j < usage["(reassigned)"].length; j++) { + if (!usage["(reassigned)"][j].ignoreW020) { + warning("W020", usage["(reassigned)"][j]); + } + } + } + } + else { + // label usage is not predefined and we have not found a declaration + // so report as undeclared + if (usage["(tokens)"]) { + for (j = 0; j < usage["(tokens)"].length; j++) { + var undefinedToken = usage["(tokens)"][j]; + // if its not a forgiven undefined (e.g. typof x) + if (!undefinedToken.forgiveUndef) { + // if undef is on and undef was on when the token was defined + if (state.option.undef && !undefinedToken.ignoreUndef) { + warning("W117", undefinedToken, usedLabelName); + } + if (impliedGlobals[usedLabelName]) { + impliedGlobals[usedLabelName].line.push(undefinedToken.line); + } else { + impliedGlobals[usedLabelName] = { + name: usedLabelName, + line: [undefinedToken.line] + }; + } + } + } + } + } + } + } + + // if exiting the global scope, we can warn about declared globals that haven't been used yet + if (!subScope) { + Object.keys(declared) + .forEach(function(labelNotUsed) { + _warnUnused(labelNotUsed, declared[labelNotUsed], "var"); + }); + } + + // If this is not a function boundary, transfer function-scoped labels to + // the parent block (a rough simulation of variable hoisting). Previously + // existing labels in the parent block should take precedence so that things and stuff. + if (subScope && !isUnstackingFunctionBody && + !isUnstackingFunctionParams && !isUnstackingFunctionOuter) { + var labelNames = Object.keys(currentLabels); + for (i = 0; i < labelNames.length; i++) { + + var defLabelName = labelNames[i]; + var defLabel = currentLabels[defLabelName]; + + if (!defLabel["(blockscoped)"] && defLabel["(type)"] !== "exception") { + var shadowed = subScope["(labels)"][defLabelName]; + + // Do not overwrite a label if it exists in the parent scope + // because it is shared by adjacent blocks. Copy the `unused` + // property so that any references found within the current block + // are counted toward that higher-level declaration. + if (shadowed) { + shadowed["(unused)"] &= defLabel["(unused)"]; + + // "Hoist" the variable to the parent block, decorating the label + // so that future references, though technically valid, can be + // reported as "out-of-scope" in the absence of the `funcscope` + // option. + } else { + defLabel["(useOutsideOfScope)"] = + // Do not warn about out-of-scope usages in the global scope + _currentFunctBody["(type)"] !== "global" && + // When a higher scope contains a binding for the label, the + // label is a re-declaration and should not prompt "used + // out-of-scope" warnings. + !this.funct.has(defLabelName, { excludeCurrent: true }); + + subScope["(labels)"][defLabelName] = defLabel; + } + + delete currentLabels[defLabelName]; + } + } + } + + _checkForUnused(); + + _scopeStack.pop(); + if (isUnstackingFunctionBody) { + _currentFunctBody = _scopeStack[_.findLastIndex(_scopeStack, function(scope) { + // if function or if global (which is at the bottom so it will only return true if we call back) + return scope["(isFuncBody)"] || scope["(type)"] === "global"; + })]; + } + + _current = subScope; + }, + + /** + * Add a param to the current scope + * @param {string} labelName + * @param {Token} token + * @param {string} [type="param"] param type + */ + addParam: function(labelName, token, type) { + type = type || "param"; + + if (type === "exception") { + // if defined in the current function + var previouslyDefinedLabelType = this.funct.labeltype(labelName); + if (previouslyDefinedLabelType && previouslyDefinedLabelType !== "exception") { + // and has not been used yet in the current function scope + if (!state.option.node) { + warning("W002", state.tokens.next, labelName); + } + } + } + + // The variable was declared in the current scope + if (_.has(_current["(labels)"], labelName)) { + _current["(labels)"][labelName].duplicated = true; + + // The variable was declared in an outer scope + } else { + // if this scope has the variable defined, it's a re-definition error + _checkOuterShadow(labelName, token, type); + + _current["(labels)"][labelName] = { + "(type)" : type, + "(token)": token, + "(unused)": true }; + + _current["(params)"].push(labelName); + } + + if (_.has(_current["(usages)"], labelName)) { + var usage = _current["(usages)"][labelName]; + // if its in a sub function it is not necessarily an error, just latedef + if (usage["(onlyUsedSubFunction)"]) { + _latedefWarning(type, labelName, token); + } else { + // this is a clear illegal usage for block scoped variables + warning("E056", token, labelName, type); + } + } + }, + + validateParams: function() { + // This method only concerns errors for function parameters + if (_currentFunctBody["(type)"] === "global") { + return; + } + + var isStrict = state.isStrict(); + var currentFunctParamScope = _currentFunctBody["(parent)"]; + + if (!currentFunctParamScope["(params)"]) { + return; + } + + currentFunctParamScope["(params)"].forEach(function(labelName) { + var label = currentFunctParamScope["(labels)"][labelName]; + + if (label && label.duplicated) { + if (isStrict) { + warning("E011", label["(token)"], labelName); + } else if (state.option.shadow !== true) { + warning("W004", label["(token)"], labelName); + } + } + }); + }, + + getUsedOrDefinedGlobals: function() { + // jshint proto: true + var list = Object.keys(usedPredefinedAndGlobals); + + // If `__proto__` is used as a global variable name, its entry in the + // lookup table may not be enumerated by `Object.keys` (depending on the + // environment). + if (usedPredefinedAndGlobals.__proto__ === marker && + list.indexOf("__proto__") === -1) { + list.push("__proto__"); + } + + return list; + }, + + /** + * Gets an array of implied globals + * @returns {Array.<{ name: string, line: Array.}>} + */ + getImpliedGlobals: function() { + // jshint proto: true + var values = _.values(impliedGlobals); + var hasProto = false; + + // If `__proto__` is an implied global variable, its entry in the lookup + // table may not be enumerated by `_.values` (depending on the + // environment). + if (impliedGlobals.__proto__) { + hasProto = values.some(function(value) { + return value.name === "__proto__"; + }); + + if (!hasProto) { + values.push(impliedGlobals.__proto__); + } + } + + return values; + }, + + /** + * Returns a list of unused variables + * @returns {Array} + */ + getUnuseds: function() { + return unuseds; + }, + + has: function(labelName) { + return Boolean(_getLabel(labelName)); + }, + + labeltype: function(labelName) { + // returns a labels type or null if not present + var scopeLabels = _getLabel(labelName); + if (scopeLabels) { + return scopeLabels[labelName]["(type)"]; + } + return null; + }, + + /** + * for the exported options, indicating a variable is used outside the file + */ + addExported: function(labelName) { + var globalLabels = _scopeStack[0]["(labels)"]; + if (_.has(declared, labelName)) { + // remove the declared token, so we know it is used + delete declared[labelName]; + } else if (_.has(globalLabels, labelName)) { + globalLabels[labelName]["(unused)"] = false; + } else { + for (var i = 1; i < _scopeStack.length; i++) { + var scope = _scopeStack[i]; + // if `scope.(type)` is not defined, it is a block scope + if (!scope["(type)"]) { + if (_.has(scope["(labels)"], labelName) && + !scope["(labels)"][labelName]["(blockscoped)"]) { + scope["(labels)"][labelName]["(unused)"] = false; + return; + } + } else { + break; + } + } + exported[labelName] = true; + } + }, + + /** + * Mark an indentifier as es6 module exported + */ + setExported: function(labelName, token) { + this.block.use(labelName, token); + }, + + /** + * adds an indentifier to the relevant current scope and creates warnings/errors as necessary + * @param {string} labelName + * @param {Object} opts + * @param {String} opts.type - the type of the label e.g. "param", "var", "let, "const", "function" + * @param {Token} opts.token - the token pointing at the declaration + */ + addlabel: function(labelName, opts) { + + var type = opts.type; + var token = opts.token; + var isblockscoped = type === "let" || type === "const" || type === "class"; + var isexported = (isblockscoped ? _current : _currentFunctBody)["(type)"] === "global" && + _.has(exported, labelName); + + // outer shadow check (inner is only on non-block scoped) + _checkOuterShadow(labelName, token, type); + + // if is block scoped (let or const) + if (isblockscoped) { + + var declaredInCurrentScope = _current["(labels)"][labelName]; + // for block scoped variables, params are seen in the current scope as the root function + // scope, so check these too. + if (!declaredInCurrentScope && _current === _currentFunctBody && + _current["(type)"] !== "global") { + declaredInCurrentScope = !!_currentFunctBody["(parent)"]["(labels)"][labelName]; + } + + // if its not already defined (which is an error, so ignore) and is used in TDZ + if (!declaredInCurrentScope && _current["(usages)"][labelName]) { + var usage = _current["(usages)"][labelName]; + // if its in a sub function it is not necessarily an error, just latedef + if (usage["(onlyUsedSubFunction)"]) { + _latedefWarning(type, labelName, token); + } else { + // this is a clear illegal usage for block scoped variables + warning("E056", token, labelName, type); + } + } + + // if this scope has the variable defined, its a re-definition error + if (declaredInCurrentScope) { + warning("E011", token, labelName); + } + else if (state.option.shadow === "outer") { + + // if shadow is outer, for block scope we want to detect any shadowing within this function + if (scopeManagerInst.funct.has(labelName)) { + warning("W004", token, labelName); + } + } + + scopeManagerInst.block.add(labelName, type, token, !isexported); + + } else { + + var declaredInCurrentFunctionScope = scopeManagerInst.funct.has(labelName); + + // check for late definition, ignore if already declared + if (!declaredInCurrentFunctionScope && usedSoFarInCurrentFunction(labelName)) { + _latedefWarning(type, labelName, token); + } + + // defining with a var or a function when a block scope variable of the same name + // is in scope is an error + if (scopeManagerInst.funct.has(labelName, { onlyBlockscoped: true })) { + warning("E011", token, labelName); + } else if (state.option.shadow !== true) { + // now since we didn't get any block scope variables, test for var/function + // shadowing + if (declaredInCurrentFunctionScope && labelName !== "__proto__") { + + // see https://github.com/jshint/jshint/issues/2400 + if (_currentFunctBody["(type)"] !== "global") { + warning("W004", token, labelName); + } + } + } + + scopeManagerInst.funct.add(labelName, type, token, !isexported); + + if (_currentFunctBody["(type)"] === "global" && !state.impliedClosure()) { + usedPredefinedAndGlobals[labelName] = marker; + } + } + }, + + funct: { + /** + * Returns the label type given certain options + * @param labelName + * @param {Object=} options + * @param {Boolean=} options.onlyBlockscoped - only include block scoped labels + * @param {Boolean=} options.excludeParams - exclude the param scope + * @param {Boolean=} options.excludeCurrent - exclude the current scope + * @returns {String} + */ + labeltype: function(labelName, options) { + var onlyBlockscoped = options && options.onlyBlockscoped; + var excludeParams = options && options.excludeParams; + var currentScopeIndex = _scopeStack.length - (options && options.excludeCurrent ? 2 : 1); + for (var i = currentScopeIndex; i >= 0; i--) { + var current = _scopeStack[i]; + if (current["(labels)"][labelName] && + (!onlyBlockscoped || current["(labels)"][labelName]["(blockscoped)"])) { + return current["(labels)"][labelName]["(type)"]; + } + var scopeCheck = excludeParams ? _scopeStack[ i - 1 ] : current; + if (scopeCheck && scopeCheck["(type)"] === "functionparams") { + return null; + } + } + return null; + }, + /** + * Returns if a break label exists in the function scope + * @param {string} labelName + * @returns {boolean} + */ + hasBreakLabel: function(labelName) { + for (var i = _scopeStack.length - 1; i >= 0; i--) { + var current = _scopeStack[i]; + + if (current["(breakLabels)"][labelName]) { + return true; + } + if (current["(type)"] === "functionparams") { + return false; + } + } + return false; + }, + /** + * Returns if the label is in the current function scope + * See scopeManager.funct.labelType for options + */ + has: function(labelName, options) { + return Boolean(this.labeltype(labelName, options)); + }, + + /** + * Adds a new function scoped variable + * see block.add for block scoped + */ + add: function(labelName, type, tok, unused) { + _current["(labels)"][labelName] = { + "(type)" : type, + "(token)": tok, + "(blockscoped)": false, + "(function)": _currentFunctBody, + "(unused)": unused }; + } + }, + + block: { + + /** + * is the current block global? + * @returns Boolean + */ + isGlobal: function() { + return _current["(type)"] === "global"; + }, + + use: function(labelName, token) { + + // if resolves to current function params, then do not store usage just resolve + // this is because function(a) { var a; a = a; } will resolve to the param, not + // to the unset var + // first check the param is used + var paramScope = _currentFunctBody["(parent)"]; + if (paramScope && paramScope["(labels)"][labelName] && + paramScope["(labels)"][labelName]["(type)"] === "param") { + + // then check its not declared by a block scope variable + if (!scopeManagerInst.funct.has(labelName, + { excludeParams: true, onlyBlockscoped: true })) { + paramScope["(labels)"][labelName]["(unused)"] = false; + } + } + + if (token && (state.ignored.W117 || state.option.undef === false)) { + token.ignoreUndef = true; + } + + _setupUsages(labelName); + + _current["(usages)"][labelName]["(onlyUsedSubFunction)"] = false; + + if (token) { + token["(function)"] = _currentFunctBody; + _current["(usages)"][labelName]["(tokens)"].push(token); + } + }, + + reassign: function(labelName, token) { + token.ignoreW020 = state.ignored.W020; + token.ignoreW021 = state.ignored.W021; + + this.modify(labelName, token); + + _current["(usages)"][labelName]["(reassigned)"].push(token); + }, + + modify: function(labelName, token) { + + _setupUsages(labelName); + + _current["(usages)"][labelName]["(onlyUsedSubFunction)"] = false; + _current["(usages)"][labelName]["(modified)"].push(token); + }, + + /** + * Adds a new variable + */ + add: function(labelName, type, tok, unused) { + _current["(labels)"][labelName] = { + "(type)" : type, + "(token)": tok, + "(blockscoped)": true, + "(unused)": unused }; + }, + + addBreakLabel: function(labelName, opts) { + var token = opts.token; + if (scopeManagerInst.funct.hasBreakLabel(labelName)) { + warning("E011", token, labelName); + } + else if (state.option.shadow === "outer") { + if (scopeManagerInst.funct.has(labelName)) { + warning("W004", token, labelName); + } else { + _checkOuterShadow(labelName, token); + } + } + _current["(breakLabels)"][labelName] = token; + } + } + }; + return scopeManagerInst; +}; + +module.exports = scopeManager; + +},{"events":5,"lodash":12}],19:[function(require,module,exports){ +"use strict"; var NameStack = require("./name-stack.js"); var state = { @@ -16478,34 +17766,117 @@ var state = { */ isStrict: function() { return this.directive["use strict"] || this.inClassBody || - this.option.module; - }, - - // Assumption: chronologically ES3 < ES5 < ES6/ESNext < Moz - inMoz: function() { - return this.option.moz && !this.option.esnext; + this.option.module || this.option.strict === "implied"; }, /** - * @param {object} options - * @param {boolean} options.strict - When `true`, only consider ESNext when - * in "esnext" code and *not* in "moz". + * Determine if the current state warrants a warning for statements outside + * of strict mode code. + * + * While emitting warnings based on function scope would be more intuitive + * (and less noisy), JSHint observes statement-based semantics in order to + * preserve legacy behavior. + * + * This method does not take the state of the parser into account, making no + * distinction between global code and function code. Because the "missing + * 'use strict'" warning is *also* reported at function boundaries, this + * function interprets `strict` option values `true` and `undefined` as + * equivalent. */ - inESNext: function(strict) { - if (strict) { - return !this.option.moz && this.option.esnext; + stmtMissingStrict: function() { + if (this.option.strict === "global") { + return true; } - return this.option.moz || this.option.esnext; + + if (this.option.strict === false) { + return false; + } + + if (this.option.globalstrict) { + return true; + } + + return false; }, - inES5: function() { - return !this.option.es3; + allowsGlobalUsd: function() { + return this.option.strict === "global" || this.option.globalstrict || + this.option.module || this.impliedClosure(); }, - inES3: function() { - return this.option.es3; + /** + * Determine if the current configuration describes an environment that is + * wrapped in an immediately-invoked function expression prior to evaluation. + * + * @returns {boolean} + */ + impliedClosure: function() { + return this.option.node || this.option.phantom || this.option.browserify; }, + // Assumption: chronologically ES3 < ES5 < ES6 < Moz + + inMoz: function() { + return this.option.moz; + }, + + /** + * @param {boolean} strict - When `true`, only consider ES6 when in + * "esversion: 6" code. + */ + inES6: function(strict) { + if (strict) { + return this.esVersion === 6; + } + return this.option.moz || this.esVersion >= 6; + }, + + /** + * @param {boolean} strict - When `true`, return `true` only when + * esVersion is exactly 5 + */ + inES5: function(strict) { + if (strict) { + return (!this.esVersion || this.esVersion === 5) && !this.option.moz; + } + return !this.esVersion || this.esVersion >= 5 || this.option.moz; + }, + + /** + * Determine the current version of the input language by inspecting the + * value of all ECMAScript-version-related options. This logic is necessary + * to ensure compatibility with deprecated options `es3`, `es5`, and + * `esnext`, and it may be drastically simplified when those options are + * removed. + * + * @returns {string|null} - the name of any incompatible option detected, + * `null` otherwise + */ + inferEsVersion: function() { + var badOpt = null; + + if (this.option.esversion) { + if (this.option.es3) { + badOpt = "es3"; + } else if (this.option.es5) { + badOpt = "es5"; + } else if (this.option.esnext) { + badOpt = "esnext"; + } + + if (badOpt) { + return badOpt; + } + + this.esVersion = this.option.esversion; + } else if (this.option.es3) { + this.esVersion = 3; + } else if (this.option.esnext) { + this.esVersion = 6; + } + + return null; + }, reset: function() { this.tokens = { @@ -16515,6 +17886,8 @@ var state = { }; this.option = {}; + this.esVersion = 5; + this.funct = null; this.ignored = {}; this.directive = {}; this.jsonMode = false; @@ -16526,15 +17899,12 @@ var state = { this.forinifcheckneeded = false; this.nameStack = new NameStack(); this.inClassBody = false; - - // Blank out non-multi-line-commented lines when ignoring linter errors - this.ignoreLinterErrors = false; } }; exports.state = state; -},{"./name-stack.js":15}],19:[function(require,module,exports){ +},{"./name-stack.js":15}],20:[function(require,module,exports){ "use strict"; exports.register = function(linter) { @@ -16550,13 +17920,14 @@ exports.register = function(linter) { linter.warn("W103", { line: data.line, char: data.char, - data: [ data.name ] + data: [ data.name, "6" ] }); } }); // Check for properties named __iterator__. This is a special property - // available only in browsers with JavaScript 1.7 implementation. + // available only in browsers with JavaScript 1.7 implementation, but + // it is deprecated for ES6 linter.on("Identifier", function style_scanIterator(data) { if (linter.getOption("iterator")) { @@ -16564,7 +17935,7 @@ exports.register = function(linter) { } if (data.name === "__iterator__") { - linter.warn("W104", { + linter.warn("W103", { line: data.line, char: data.char, data: [ data.name ] @@ -16583,7 +17954,7 @@ exports.register = function(linter) { if (data.name.replace(/^_+|_+$/g, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { linter.warn("W106", { line: data.line, - char: data.from, + char: data.char, data: [ data.name ] }); } @@ -16679,7 +18050,7 @@ exports.register = function(linter) { }); }; -},{}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ // jshint -W001 "use strict"; @@ -16755,6 +18126,7 @@ exports.browser = { close : false, closed : false, Comment : false, + CompositionEvent : false, CustomEvent : false, DOMParser : false, defaultStatus : false, @@ -16766,6 +18138,8 @@ exports.browser = { Event : false, event : false, fetch : false, + File : false, + FileList : false, FileReader : false, FormData : false, focus : false, @@ -16779,6 +18153,7 @@ exports.browser = { HTMLBRElement : false, HTMLButtonElement : false, HTMLCanvasElement : false, + HTMLCollection : false, HTMLDirectoryElement : false, HTMLDivElement : false, HTMLDListElement : false, @@ -16858,6 +18233,7 @@ exports.browser = { opener : false, Option : false, parent : false, + performance : false, print : false, Range : false, requestAnimationFrame : false, @@ -17047,6 +18423,7 @@ exports.browser = { WebGLUniformLocation : false, WebSocket : false, window : false, + Window : false, Worker : false, XDomainRequest : false, XMLHttpRequest : false, @@ -17147,6 +18524,7 @@ exports.qunit = { module : false, notDeepEqual : false, notEqual : false, + notOk : false, notPropEqual : false, notStrictEqual : false, ok : false, @@ -17352,6 +18730,8 @@ exports.yui = { }; exports.mocha = { + // Global (for config etc.) + mocha : false, // BDD describe : false, xdescribe : false, @@ -17393,7 +18773,8 @@ exports.jasmine = { afterAll : false, fail : false, fdescribe : false, - fit : false + fit : false, + pending : false }; },{}],"jshint":[function(require,module,exports){ @@ -17431,15 +18812,16 @@ exports.jasmine = { /*global console:true */ /*exported console */ -var _ = require("lodash"); -var events = require("events"); -var vars = require("./vars.js"); -var messages = require("./messages.js"); -var Lexer = require("./lex.js").Lexer; -var reg = require("./reg.js"); -var state = require("./state.js").state; -var style = require("./style.js"); -var options = require("./options.js"); +var _ = require("lodash"); +var events = require("events"); +var vars = require("./vars.js"); +var messages = require("./messages.js"); +var Lexer = require("./lex.js").Lexer; +var reg = require("./reg.js"); +var state = require("./state.js").state; +var style = require("./style.js"); +var options = require("./options.js"); +var scopeManager = require("./scope-manager.js"); // We need this module here because environments such as IE and Rhino // don't necessarilly expose the 'console' API and browserify uses @@ -17473,18 +18855,14 @@ var JSHINT = (function() { }, declared, // Globals that were declared using /*global ... */ syntax. - exported, // Variables that are used outside of the current file. functionicity = [ "closure", "exception", "global", "label", "outer", "unused", "var" ], - funct, // The current function functions, // All of the functions - global, // The global scope - implied, // Implied globals inblock, indent, lookahead, @@ -17493,9 +18871,7 @@ var JSHINT = (function() { membersOnly, predefined, // Global variables defined by option - scope, // The current scope stack, - unuseds, urls, extraModules = [], @@ -17538,22 +18914,28 @@ var JSHINT = (function() { } var meta = token.meta; - if (meta && meta.isFutureReservedWord && state.inES5()) { - // ES3 FutureReservedWord in an ES5 environment. - if (!meta.es5) { + if (meta && meta.isFutureReservedWord) { + if (meta.moduleOnly && !state.option.module) { return false; } - // Some ES5 FutureReservedWord identifiers are active only - // within a strict mode environment. - if (meta.strictOnly) { - if (!state.option.strict && !state.isStrict()) { + if (state.inES5()) { + // ES3 FutureReservedWord in an ES5 environment. + if (!meta.es5) { return false; } - } - if (token.isProperty) { - return false; + // Some ES5 FutureReservedWord identifiers are active only + // within a strict mode environment. + if (meta.strictOnly) { + if (!state.option.strict && !state.isStrict()) { + return false; + } + } + + if (token.isProperty) { + return false; + } } } @@ -17591,28 +18973,38 @@ var JSHINT = (function() { } function assume() { + var badESOpt = null; processenforceall(); - if (!state.option.es3) { + /** + * TODO: Remove in JSHint 3 + */ + badESOpt = state.inferEsVersion(); + if (badESOpt) { + quit("E059", state.tokens.next, "esversion", badESOpt); + } + + if (state.inES5()) { combine(predefined, vars.ecmaIdentifiers[5]); } - if (state.option.esnext) { + if (state.inES6()) { combine(predefined, vars.ecmaIdentifiers[6]); } + /** + * Use `in` to check for the presence of any explicitly-specified value for + * `globalstrict` because both `true` and `false` should trigger an error. + */ + if (state.option.strict === "global" && "globalstrict" in state.option) { + quit("E059", state.tokens.next, "strict", "globalstrict"); + } + if (state.option.module) { - /** - * TODO: Extend this restriction to *all* "environmental" options. - */ - if (!hasParsedCode(funct)) { - error("E055", state.tokens.next, "module"); - } - /** * TODO: Extend this restriction to *all* ES6-specific options. */ - if (!state.inESNext()) { + if (!state.inES6()) { warning("W134", state.tokens.next, "module", 6); } } @@ -17693,10 +19085,6 @@ var JSHINT = (function() { combine(predefined, vars.wsh); } - if (state.option.globalstrict && state.option.strict !== false) { - state.option.strict = true; - } - if (state.option.yui) { combine(predefined, vars.yui); } @@ -17707,24 +19095,25 @@ var JSHINT = (function() { } // Produce an error warning. - function quit(code, line, chr) { - var percentage = Math.floor((line / state.lines.length) * 100); + function quit(code, token, a, b) { + var percentage = Math.floor((token.line / state.lines.length) * 100); var message = messages.errors[code].desc; - throw { + var exception = { name: "JSHintError", - line: line, - character: chr, + line: token.line, + character: token.from, message: message + " (" + percentage + "% scanned).", raw: message, - code: code + code: code, + a: a, + b: b }; - } - function isundef(scope, code, token, a) { - if (!state.ignored[code] && state.option.undef !== false) { - JSHINT.undefs.push([scope, code, token, a]); - } + exception.reason = supplant(message, exception) + " (" + percentage + + "% scanned)."; + + throw exception; } function removeIgnoredMessages() { @@ -17753,8 +19142,8 @@ var JSHINT = (function() { t = state.tokens.curr; } - l = t.line || 0; - ch = t.from || 0; + l = t.line; + ch = t.from; w = { id: "(error)", @@ -17776,7 +19165,7 @@ var JSHINT = (function() { removeIgnoredMessages(); if (JSHINT.errors.length >= state.option.maxerr) - quit("E043", l, ch); + quit("E043", t); return w; } @@ -17811,88 +19200,26 @@ var JSHINT = (function() { return i; } - // adds an indentifier to the relevant current scope and creates warnings/errors as necessary - // name: string - // opts: { type: string, token: token, isblockscoped: bool } - function addlabel(name, opts) { - - var type = opts.type; - var token = opts.token; - var isblockscoped = opts.isblockscoped; - - // Define label in the current function in the current scope. - if (type === "exception") { - if (_.has(funct["(context)"], name)) { - if (funct[name] !== true && !state.option.node) { - warning("W002", state.tokens.next, name); - } - } - } - - if (_.has(funct, name) && !funct["(global)"]) { - if (funct[name] === true) { - if (state.option.latedef) { - if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || - !_.contains([funct[name], type], "unction")) { - warning("W003", state.tokens.next, name); - } - } - } else { - if ((!state.option.shadow || _.contains([ "inner", "outer" ], state.option.shadow)) && - type !== "exception" || funct["(blockscope)"].getlabel(name)) { - warning("W004", state.tokens.next, name); - } - } - } - - if (funct["(context)"] && _.has(funct["(context)"], name) && type !== "function") { - if (state.option.shadow === "outer") { - warning("W123", state.tokens.next, name); - } - } - - // if the identifier is blockscoped (a let or a const), add it only to the current blockscope - if (isblockscoped) { - funct["(blockscope)"].current.add(name, type, state.tokens.curr); - if (funct["(blockscope)"].atTop() && exported[name]) { - state.tokens.curr.exported = true; - } - } else { - funct["(blockscope)"].shadow(name); - funct[name] = type; - - if (token) { - funct["(tokens)"][name] = token; - } - - if (funct["(global)"]) { - global[name] = funct; - if (_.has(implied, name)) { - if (state.option.latedef) { - if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || - !_.contains([funct[name], type], "unction")) { - warning("W003", state.tokens.next, name); - } - } - - delete implied[name]; - } - } else { - scope[name] = funct; - } - } - } - function doOption() { var nt = state.tokens.next; var body = nt.body.split(",").map(function(s) { return s.trim(); }); + var predef = {}; if (nt.type === "globals") { - body.forEach(function(g) { + body.forEach(function(g, idx) { g = g.split(":"); var key = (g[0] || "").trim(); var val = (g[1] || "").trim(); + if (key === "-" || !key.length) { + // Ignore trailing comma + if (idx > 0 && idx === body.length - 1) { + return; + } + error("E002", nt); + return; + } + if (key.charAt(0) === "-") { key = key.slice(1); val = false; @@ -17914,8 +19241,17 @@ var JSHINT = (function() { } if (nt.type === "exported") { - body.forEach(function(e) { - exported[e] = true; + body.forEach(function(e, idx) { + if (!e.length) { + // Ignore trailing comma + if (idx > 0 && idx === body.length - 1) { + return; + } + error("E002", nt); + return; + } + + state.funct["(scope)"].addExported(e); }); } @@ -17974,16 +19310,10 @@ var JSHINT = (function() { return; } - if (key === "es5") { - if (val === "true" && state.option.es5) { - warning("I003"); - } - } - if (key === "validthis") { // `validthis` is valid only within a function scope. - if (funct["(global)"]) + if (state.funct["(global)"]) return void error("E009"); if (val !== "true" && val !== "false") @@ -18064,12 +19394,6 @@ var JSHINT = (function() { if (key === "ignore") { switch (val) { - case "start": - state.ignoreLinterErrors = true; - break; - case "end": - state.ignoreLinterErrors = false; - break; case "line": state.ignoredLines[nt.line] = true; removeIgnoredMessages(); @@ -18080,6 +19404,54 @@ var JSHINT = (function() { return; } + if (key === "strict") { + switch (val) { + case "true": + state.option.strict = true; + break; + case "false": + state.option.strict = false; + break; + case "global": + case "implied": + state.option.strict = val; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "module") { + /** + * TODO: Extend this restriction to *all* "environmental" options. + */ + if (!hasParsedCode(state.funct)) { + error("E055", state.tokens.next, "module"); + } + } + + if (key === "esversion") { + switch (val) { + case "3": + case "5": + case "6": + state.option.moz = false; + state.option.esversion = +val; + break; + case "2015": + state.option.moz = false; + state.option.esversion = 6; + break; + default: + error("E002", nt); + } + if (!hasParsedCode(state.funct)) { + error("E055", state.tokens.next, "esversion"); + } + return; + } + var match = /^([+-])(W\d{3})$/g.exec(key); if (match) { // ignore for -W..., unignore for +W... @@ -18100,9 +19472,6 @@ var JSHINT = (function() { state.option[key] = (val === "true"); } - if (key === "newcap") { - state.option["(explicitNewcap)"] = true; - } return; } @@ -18120,7 +19489,11 @@ var JSHINT = (function() { // for ( var i = ... function peek(p) { - var i = p || 0, j = 0, t; + var i = p || 0, j = lookahead.length, t; + + if (i < j) { + return lookahead[i]; + } while (j <= i) { t = lookahead[j]; @@ -18187,7 +19560,7 @@ var JSHINT = (function() { state.tokens.next = lookahead.shift() || lex.token(); if (!state.tokens.next) { // No more tokens left, give up - quit("E041", state.tokens.curr.line); + quit("E041", state.tokens.curr); } if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { @@ -18199,7 +19572,11 @@ var JSHINT = (function() { } if (state.tokens.next.isSpecial) { - doOption(); + if (state.tokens.next.type === "falls through") { + state.tokens.curr.caseFallsThrough = true; + } else { + doOption(); + } } else { if (state.tokens.next.id !== "(endline)") { break; @@ -18254,7 +19631,7 @@ var JSHINT = (function() { } isLetExpr = true; // create a new block scope we use only for the current expression - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); advance("let"); advance("("); state.tokens.prev.fud(); @@ -18276,7 +19653,7 @@ var JSHINT = (function() { advance(); if (initial) { - funct["(verb)"] = state.tokens.curr.value; + state.funct["(verb)"] = state.tokens.curr.value; state.tokens.curr.beginsStmt = true; } @@ -18289,9 +19666,7 @@ var JSHINT = (function() { error("E030", state.tokens.curr, state.tokens.curr.id); } - // TODO: use pratt mechanics rather than special casing template tokens - while ((rbp < state.tokens.next.lbp || state.tokens.next.type === "(template)") && - !isEndOfExpr()) { + while (rbp < state.tokens.next.lbp && !isEndOfExpr()) { isArray = state.tokens.curr.value === "Array"; isObject = state.tokens.curr.value === "Object"; @@ -18331,7 +19706,7 @@ var JSHINT = (function() { } } if (isLetExpr) { - funct["(blockscope)"].unstack(); + state.funct["(scope)"].unstack(); } state.nameStack.pop(); @@ -18364,16 +19739,16 @@ var JSHINT = (function() { function nobreakcomma(left, right) { if (left.line !== startLine(right)) { if (!state.option.laxcomma) { - if (comma.first) { + if (parseComma.first) { warning("I001"); - comma.first = false; + parseComma.first = false; } warning("W014", left, right.value); } } } - function comma(opts) { + function parseComma(opts) { opts = opts || {}; if (!opts.peek) { @@ -18487,12 +19862,12 @@ var JSHINT = (function() { warning("W017", this); } + if (this.right && this.right.isMetaProperty) { + error("E031", this); // detect increment/decrement of a const // in the case of a.b, right will be the "." punctuator - if (this.right && this.right.identifier) { - if (funct["(blockscope)"].labeltype(this.right.value) === "const") { - error("E013", this, this.right.value); - } + } else if (this.right && this.right.identifier) { + state.funct["(scope)"].block.modify(this.right.value, this); } } @@ -18563,7 +19938,6 @@ var JSHINT = (function() { return x; } - function application(s) { var x = symbol(s, 42); @@ -18592,7 +19966,7 @@ var JSHINT = (function() { } if (!left || !right) { - quit("E041", state.tokens.curr.line); + quit("E041", state.tokens.curr); } if (left.id === "!") { @@ -18649,7 +20023,7 @@ var JSHINT = (function() { if (!left || !right) return false; - values = state.inESNext() ? typeofValues.es6 : typeofValues.es3; + values = state.inES6() ? typeofValues.es6 : typeofValues.es3; if (right.type === "(identifier)" && right.value === "typeof" && left.type === "(string)") return !_.contains(values, left.value); @@ -18657,11 +20031,11 @@ var JSHINT = (function() { return false; } - function isGlobalEval(left, state, funct) { + function isGlobalEval(left, state) { var isGlobal = false; // permit methods to refer to an "eval" key in their own context - if (left.type === "this" && funct["(context)"] === null) { + if (left.type === "this" && state.funct["(context)"] === null) { isGlobal = true; } // permit use of "eval" members of objects @@ -18698,78 +20072,99 @@ var JSHINT = (function() { while (!obj.identifier && typeof obj.left === "object") obj = obj.left; - if (obj.identifier && natives.indexOf(obj.value) >= 0) + if (obj.identifier && natives.indexOf(obj.value) >= 0 && + state.funct["(scope)"].isPredefined(obj.value)) { return obj.value; + } } var prototype = walkPrototype(left); if (prototype) return walkNative(prototype); } + /** + * Checks the left hand side of an assignment for issues, returns if ok + * @param {token} left - the left hand side of the assignment + * @param {token=} assignToken - the token for the assignment, used for reporting + * @param {object=} options - optional object + * @param {boolean} options.allowDestructuring - whether to allow destructuting binding + * @returns {boolean} Whether the left hand side is OK + */ + function checkLeftSideAssign(left, assignToken, options) { + + var allowDestructuring = options && options.allowDestructuring; + + assignToken = assignToken || left; + + if (state.option.freeze) { + var nativeObject = findNativePrototype(left); + if (nativeObject) + warning("W121", left, nativeObject); + } + if (checkPunctuator(left, "...")) { + left = left.right; + } + + if (left.identifier && !left.isMetaProperty) { + // reassign also calls modify + // but we are specific in order to catch function re-assignment + // and globals re-assignment + state.funct["(scope)"].block.reassign(left.value, left); + } + + if (left.id === ".") { + if (!left.left || left.left.value === "arguments" && !state.isStrict()) { + warning("E031", assignToken); + } + + state.nameStack.set(state.tokens.prev); + return true; + } else if (left.id === "{" || left.id === "[") { + if (allowDestructuring && left.destructAssign) { + left.destructAssign.forEach(function(t) { + if (t.id) { + state.funct["(scope)"].block.modify(t.id, t.token); + } + }); + } else { + if (left.id === "{" || !left.left) { + warning("E031", assignToken); + } else if (left.left.value === "arguments" && !state.isStrict()) { + warning("E031", assignToken); + } + } + + if (left.id === "[") { + state.nameStack.set(left.right); + } + + return true; + } else if (left.identifier && !isReserved(left) && !left.isMetaProperty) { + if (state.funct["(scope)"].labeltype(left.value) === "exception") { + warning("W022", left); + } + state.nameStack.set(left); + return true; + } + + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); + } else { + error("E031", assignToken); + } + + return false; + } + function assignop(s, f, p) { var x = infix(s, typeof f === "function" ? f : function(left, that) { that.left = left; - if (left) { - if (state.option.freeze) { - var nativeObject = findNativePrototype(left); - if (nativeObject) - warning("W121", left, nativeObject); - } + checkLeftSideAssign(left, that, { allowDestructuring: true }); - if (predefined[left.value] === false && - scope[left.value]["(global)"] === true) { - warning("W020", left); - } else if (left["function"]) { - warning("W021", left, left.value); - } + that.right = expression(10); - if (funct["(blockscope)"].labeltype(left.value) === "const") { - error("E013", left, left.value); - } - - if (left.id === ".") { - if (!left.left) { - warning("E031", that); - } else if (left.left.value === "arguments" && !state.isStrict()) { - warning("E031", that); - } - - state.nameStack.set(state.tokens.prev); - that.right = expression(10); - return that; - } else if (left.id === "[") { - if (state.tokens.curr.left.first) { - state.tokens.curr.left.first.forEach(function(t) { - if (t && funct[t.value] === "const") { - error("E013", t, t.value); - } - }); - } else if (!left.left) { - warning("E031", that); - } else if (left.left.value === "arguments" && !state.isStrict()) { - warning("E031", that); - } - - state.nameStack.set(left.right); - - that.right = expression(10); - return that; - } else if (left.identifier && !isReserved(left)) { - if (funct[left.value] === "exception") { - warning("W022", left); - } - state.nameStack.set(left); - that.right = expression(10); - return that; - } - - if (left === state.syntax["function"]) { - warning("W023", state.tokens.curr); - } - } - - error("E031", that); + return that; }, p); x.exps = true; @@ -18792,25 +20187,17 @@ var JSHINT = (function() { return x; } - function bitwiseassignop(s) { return assignop(s, function(left, that) { if (state.option.bitwise) { warning("W016", that, that.id); } - if (left) { - if (left.id === "." || left.id === "[" || - (left.identifier && !isReserved(left))) { - expression(10); - return that; - } - if (left === state.syntax["function"]) { - warning("W023", state.tokens.curr); - } - return that; - } - error("E031", that); + checkLeftSideAssign(left, that); + + that.right = expression(10); + + return that; }, 20); } @@ -18826,12 +20213,12 @@ var JSHINT = (function() { warning("W017", this); } + if (left.isMetaProperty) { + error("E031", this); // detect increment/decrement of a const // in the case of a.b, left will be the "." punctuator - if (left && left.identifier) { - if (funct["(blockscope)"].labeltype(left.value) === "const") { - error("E013", this, left.value); - } + } else if (left && left.identifier) { + state.funct["(scope)"].block.modify(left.value, left); } this.left = left; @@ -18885,14 +20272,14 @@ var JSHINT = (function() { // parameter destructuring with rest operator if (state.tokens.next.value === "...") { - if (!state.option.esnext) { - warning("W119", state.tokens.next, "spread/rest operator"); + if (!state.inES6(true)) { + warning("W119", state.tokens.next, "spread/rest operator", "6"); } advance(); - if (checkPunctuators(state.tokens.next, ["..."])) { + if (checkPunctuator(state.tokens.next, "...")) { warning("E024", state.tokens.next, "..."); - while (checkPunctuators(state.tokens.next, ["..."])) { + while (checkPunctuator(state.tokens.next, "...")) { advance(); } } @@ -18949,12 +20336,18 @@ var JSHINT = (function() { if (state.tokens.next.id !== ";") { // don't complain about unclosed templates / strings if (state.tokens.next.isUnclosed) return advance(); - if (!state.option.asi) { + + var sameLine = startLine(state.tokens.next) === state.tokens.curr.line && + state.tokens.next.id !== "(end)"; + var blockEnd = checkPunctuator(state.tokens.next, "}"); + + if (sameLine && !blockEnd) { + errorAt("E058", state.tokens.curr.line, state.tokens.curr.character); + } else if (!state.option.asi) { // If this is the last statement in a block that ends on // the same line *and* option lastsemic is on, ignore the warning. // Otherwise, complain about missing semicolon. - if (!state.option.lastsemic || state.tokens.next.id !== "}" || - startLine(state.tokens.next) !== state.tokens.curr.line) { + if ((blockEnd && !state.option.lastsemic) || !sameLine) { warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); } } @@ -18964,7 +20357,7 @@ var JSHINT = (function() { } function statement() { - var i = indent, r, s = scope, t = state.tokens.next; + var i = indent, r, t = state.tokens.next, hasOwnScope = false; if (t.id === ";") { advance(";"); @@ -18983,28 +20376,13 @@ var JSHINT = (function() { res = false; } - // detect a module import declaration - if (t.value === "module" && t.type === "(identifier)") { - if (peek().type === "(identifier)") { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "module"); - } - - advance("module"); - var name = identifier(); - addlabel(name, { type: "unused", token: state.tokens.curr }); - advance("from"); - advance("(string)"); - parseFinalSemicolon(); - return; - } - } - if (t.identifier && !res && peek().id === ":") { advance(); advance(":"); - scope = Object.create(s); - addlabel(t.value, { type: "label" }); + + hasOwnScope = true; + state.funct["(scope)"].stack(); + state.funct["(scope)"].block.addBreakLabel(t.value, { token: state.tokens.curr }); if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { warning("W028", state.tokens.next, t.value, state.tokens.next.value); @@ -19024,7 +20402,7 @@ var JSHINT = (function() { // ... // } // } - var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":"); + var iscase = (state.funct["(verb)"] === "case" && state.tokens.curr.value === ":"); block(true, true, false, false, iscase); return; } @@ -19033,10 +20411,10 @@ var JSHINT = (function() { r = expression(0, true); - if (r && (!r.identifier || r.value !== "function") && (r.type !== "(punctuator)")) { - if (!state.isStrict() && - state.option.globalstrict && - state.option.strict) { + if (r && !(r.identifier && r.value === "function") && + !(r.type === "(punctuator)" && r.left && + r.left.identifier && r.left.value === "function")) { + if (!state.isStrict() && state.stmtMissingStrict()) { warning("E007"); } } @@ -19056,7 +20434,9 @@ var JSHINT = (function() { // Restore the indentation. indent = i; - scope = s; + if (hasOwnScope) { + state.funct["(scope)"].unstack(); + } return r; } @@ -19100,36 +20480,35 @@ var JSHINT = (function() { p = pn; } else if (pn.value === "[" || pn.value === ".") { // string -> [ | . is a valid production - return; + break; } else if (!state.option.asi || pn.value === "(") { // string -> ( is not a valid production warning("W033", state.tokens.next); } } else if (p.id === "." || p.id === "[") { - return; + break; } else if (p.id !== ";") { warning("W033", p); } advance(); - if (state.directive[state.tokens.curr.value]) { - warning("W034", state.tokens.curr, state.tokens.curr.value); - } - - if (state.tokens.curr.value === "use strict") { - if (!state.option["(explicitNewcap)"]) { - state.option.newcap = true; - } - state.option.undef = true; + var directive = state.tokens.curr.value; + if (state.directive[directive] || + (directive === "use strict" && state.option.strict === "implied")) { + warning("W034", state.tokens.curr, directive); } // there's no directive negation, so always set to true - state.directive[state.tokens.curr.value] = true; + state.directive[directive] = true; if (p.id === ";") { advance(";"); } } + + if (state.isStrict()) { + state.option.undef = true; + } } @@ -19148,19 +20527,15 @@ var JSHINT = (function() { b = inblock, old_indent = indent, m, - s = scope, t, line, d; inblock = ordinary; - if (!ordinary || !state.option.funcscope) - scope = Object.create(scope); - t = state.tokens.next; - var metrics = funct["(metrics)"]; + var metrics = state.funct["(metrics)"]; metrics.nestedBlockDepth += 1; metrics.verifyMaxNestedBlockDepthPerFunction(); @@ -19168,7 +20543,8 @@ var JSHINT = (function() { advance("{"); // create a new block scope - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); + state.funct["(noblockscopedvar)"] = false; line = state.tokens.curr.line; if (state.tokens.next.id !== "}") { @@ -19186,7 +20562,7 @@ var JSHINT = (function() { } directives(); - if (state.option.strict && funct["(context)"]["(global)"]) { + if (state.option.strict && state.funct["(context)"]["(global)"]) { if (!m["use strict"] && !state.isStrict()) { warning("E007"); } @@ -19197,20 +20573,25 @@ var JSHINT = (function() { metrics.statementCount += a.length; - if (isfunc) { - state.directive = m; - } - indent -= state.option.indent; } advance("}", t); - funct["(blockscope)"].unstack(); + if (isfunc) { + state.funct["(scope)"].validateParams(); + if (m) { + state.directive = m; + } + } + + state.funct["(scope)"].unstack(); indent = old_indent; } else if (!ordinary) { if (isfunc) { + state.funct["(scope)"].stack(); + m = {}; if (stmt && !isfatarrow && !state.inMoz()) { error("W118", state.tokens.curr, "function closure expressions"); @@ -19225,18 +20606,22 @@ var JSHINT = (function() { } expression(10); - if (state.option.strict && funct["(context)"]["(global)"]) { + if (state.option.strict && state.funct["(context)"]["(global)"]) { if (!m["use strict"] && !state.isStrict()) { warning("E007"); } } + + state.funct["(scope)"].unstack(); } else { error("E021", state.tokens.next, "{", state.tokens.next.value); } } else { // check to avoid let declaration not within a block - funct["(noblockscopedvar)"] = true; + // though is fine inside for loop initializer section + state.funct["(noblockscopedvar)"] = state.tokens.next.id !== "for"; + state.funct["(scope)"].stack(); if (!stmt || state.option.curly) { warning("W116", state.tokens.next, "{", state.tokens.next.value); @@ -19248,11 +20633,12 @@ var JSHINT = (function() { a = [statement()]; indent -= state.option.indent; - delete funct["(noblockscopedvar)"]; + state.funct["(scope)"].unstack(); + delete state.funct["(noblockscopedvar)"]; } // Don't clear and let it propagate out if it is "break", "return" or similar in switch case - switch (funct["(verb)"]) { + switch (state.funct["(verb)"]) { case "break": case "continue": case "return": @@ -19263,10 +20649,9 @@ var JSHINT = (function() { /* falls through */ default: - funct["(verb)"] = null; + state.funct["(verb)"] = null; } - if (!ordinary || !state.option.funcscope) scope = s; inblock = b; if (ordinary && state.option.noempty && (!a || a.length === 0)) { warning("W035", state.tokens.prev); @@ -19287,18 +20672,6 @@ var JSHINT = (function() { } } - - function note_implied(tkn) { - var name = tkn.value; - var desc = Object.getOwnPropertyDescriptor(implied, name); - - if (!desc) - implied[name] = [tkn.line]; - else - desc.value.push(tkn.line); - } - - // Build the syntax table by declaring the syntactic elements of the language. type("(number)", function() { @@ -19316,11 +20689,6 @@ var JSHINT = (function() { nud: function() { var v = this.value; - // s will be either the function object 'funct' that the identifier points at - // or it will be a boolean if it is a predefined variable - var s = scope[v]; - var f; - var block; // If this identifier is the lone parameter to a shorthand "fat arrow" // function definition, i.e. @@ -19334,107 +20702,8 @@ var JSHINT = (function() { return this; } - block = funct["(blockscope)"].getlabel(v); - - if (typeof s === "function") { - // Protection against accidental inheritance. - s = undefined; - } else if (!block && typeof s === "boolean") { - f = funct; - funct = functions[0]; - addlabel(v, { type: "var" }); - s = funct; - funct = f; - } - - // The name is in scope and defined in the current function or it exists in the blockscope. - if (funct === s || block) { - // Change 'unused' to 'var', and reject labels. - // the name is in a block scope. - switch (block ? block[v]["(type)"] : funct[v]) { - case "unused": - if (block) block[v]["(type)"] = "var"; - else funct[v] = "var"; - break; - case "unction": - if (block) block[v]["(type)"] = "function"; - else funct[v] = "function"; - this["function"] = true; - break; - case "const": - // consts can only exist inside block - // because they are never added to the scope, s - block[v]["(unused)"] = false; - break; - case "function": - this["function"] = true; - break; - case "label": - warning("W037", state.tokens.curr, v); - break; - } - } else { - // If the name is already defined in the current - // function, but not as outer, then there is a scope error. - - switch (funct[v]) { - case "closure": - case "function": - case "var": - case "unused": - warning("W038", state.tokens.curr, v); - break; - case "label": - warning("W037", state.tokens.curr, v); - break; - case "outer": - case "global": - break; - default: - // If the name is defined in an outer function, make an outer entry, - // and if it was unused, make it var. - if (s === true) { - funct[v] = true; - } else if (s === null) { - warning("W039", state.tokens.curr, v); - note_implied(state.tokens.curr); - } else if (typeof s !== "object") { - // if we're in a list comprehension, variables are declared - // locally and used before being defined. So we check - // the presence of the given variable in the comp array - // before declaring it undefined. - - if (!funct["(comparray)"].check(v)) { - isundef(funct, "W117", state.tokens.curr, v); - } - - // Explicitly mark the variable as used within function scopes - if (!funct["(global)"]) { - funct[v] = true; - } - - note_implied(state.tokens.curr); - } else { - switch (s[v]) { - case "function": - case "unction": - this["function"] = true; - s[v] = "closure"; - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "var": - case "unused": - s[v] = "closure"; - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "closure": - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "label": - warning("W037", state.tokens.curr, v); - } - } - } + if (!state.funct["(comparray)"].check(v)) { + state.funct["(scope)"].block.use(v, state.tokens.curr); } return this; }, @@ -19445,11 +20714,11 @@ var JSHINT = (function() { }; var baseTemplateSyntax = { - lbp: 0, identifier: false, template: true, }; state.syntax["(template)"] = _.extend({ + lbp: 155, type: "(template)", nud: doTemplateLiteral, led: doTemplateLiteral, @@ -19457,18 +20726,21 @@ var JSHINT = (function() { }, baseTemplateSyntax); state.syntax["(template middle)"] = _.extend({ + lbp: 0, type: "(template middle)", middle: true, noSubst: false }, baseTemplateSyntax); state.syntax["(template tail)"] = _.extend({ + lbp: 0, type: "(template tail)", tail: true, noSubst: false }, baseTemplateSyntax); state.syntax["(no subst template)"] = _.extend({ + lbp: 155, type: "(template)", nud: doTemplateLiteral, led: doTemplateLiteral, @@ -19483,7 +20755,9 @@ var JSHINT = (function() { // ECMAScript parser delim("(endline)"); - delim("(begin)"); + (function(x) { + x.line = x.from = 0; + })(delim("(begin)")); delim("(end)").reach = true; delim("(error)").reach = true; delim("}").reach = true; @@ -19501,7 +20775,7 @@ var JSHINT = (function() { reserve("default").reach = true; reserve("finally"); reservevar("arguments", function(x) { - if (state.isStrict() && funct["(global)"]) { + if (state.isStrict() && state.funct["(global)"]) { warning("E008", x); } }); @@ -19511,8 +20785,8 @@ var JSHINT = (function() { reservevar("null"); reservevar("this", function(x) { if (state.isStrict() && !isMethod() && - !state.option.validthis && ((funct["(statement)"] && - funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { + !state.option.validthis && ((state.funct["(statement)"] && + state.funct["(name)"].charAt(0) > "Z") || state.funct["(global)"])) { warning("W040", x); } }); @@ -19542,7 +20816,7 @@ var JSHINT = (function() { warning("W127"); } - if (!comma({ peek: true })) { + if (!parseComma({ peek: true })) { return that; } while (true) { @@ -19550,7 +20824,7 @@ var JSHINT = (function() { break; } that.exprs.push(expr); - if (state.tokens.next.value !== "," || !comma()) { + if (state.tokens.next.value !== "," || !parseComma()) { break; } } @@ -19578,7 +20852,8 @@ var JSHINT = (function() { bitwise("^", "bitxor", 80); bitwise("&", "bitand", 90); relation("==", function(left, right) { - var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); + var eqnull = state.option.eqnull && + ((left && left.value) === "null" || (right && right.value) === "null"); switch (true) { case !eqnull && state.option.eqeqeq: @@ -19611,7 +20886,7 @@ var JSHINT = (function() { }); relation("!=", function(left, right) { var eqnull = state.option.eqnull && - (left.value === "null" || right.value === "null"); + ((left && left.value) === "null" || (right && right.value) === "null"); if (!eqnull && state.option.eqeqeq) { this.from = this.character; @@ -19722,13 +20997,13 @@ var JSHINT = (function() { warning("W016", this, "~"); } this.arity = "unary"; - expression(150); + this.right = expression(150); return this; }); prefix("...", function() { - if (!state.option.esnext) { - warning("W119", this, "spread/rest operator"); + if (!state.inES6(true)) { + warning("W119", this, "spread/rest operator", "6"); } // TODO: Allow all AssignmentExpression @@ -19768,7 +21043,7 @@ var JSHINT = (function() { error("E030", state.tokens.next, state.tokens.next.value); } - expression(150); + this.right = expression(150); return this; }); @@ -19777,7 +21052,7 @@ var JSHINT = (function() { this.right = expression(150); if (!this.right) { // '!' followed by nothing? Give up. - quit("E041", this.line || 0); + quit("E041", this); } if (bang[this.right.id] === true) { @@ -19788,7 +21063,11 @@ var JSHINT = (function() { prefix("typeof", (function() { var p = expression(150); - this.first = p; + this.first = this.right = p; + + if (!p) { // 'typeof' followed by nothing? Give up. + quit("E041", this); + } // The `typeof` operator accepts unresolvable references, so the operand // may be undefined. @@ -19798,6 +21077,22 @@ var JSHINT = (function() { return this; })); prefix("new", function() { + var mp = metaProperty("target", function() { + if (!state.inES6(true)) { + warning("W119", state.tokens.prev, "new.target", "6"); + } + var inFunction, c = state.funct; + while (c) { + inFunction = !c["(global)"]; + if (!c["(arrow)"]) { break; } + c = c["(context)"]; + } + if (!inFunction) { + warning("W136", state.tokens.prev, "new.target"); + } + }); + if (mp) { return mp; } + var c = expression(155), i; if (c && c.id !== "function") { if (c.identifier) { @@ -19811,7 +21106,7 @@ var JSHINT = (function() { warning("W053", state.tokens.prev, c.value); break; case "Symbol": - if (state.option.esnext) { + if (state.inES6()) { warning("W053", state.tokens.prev, c.value); } break; @@ -19827,7 +21122,8 @@ var JSHINT = (function() { default: if (c.id !== "function") { i = c.value.substr(0, 1); - if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { + if (state.option.newcap && (i < "A" || i > "Z") && + !state.funct["(scope)"].isPredefined(c.value)) { warning("W055", state.tokens.curr); } } @@ -19844,7 +21140,7 @@ var JSHINT = (function() { if (state.tokens.next.id !== "(" && !state.option.supernew) { warning("W058", state.tokens.curr, state.tokens.curr.value); } - this.first = c; + this.first = this.right = c; return this; }); state.syntax["new"].exps = true; @@ -19876,7 +21172,7 @@ var JSHINT = (function() { } if (!state.option.evil && (m === "eval" || m === "execScript")) { - if (isGlobalEval(left, state, funct)) { + if (isGlobalEval(left, state)) { warning("W061"); } } @@ -19895,7 +21191,7 @@ var JSHINT = (function() { if (left) { if (left.type === "(identifier)") { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { - if ("Number String Boolean Date Object Error Symbol".indexOf(left.value) === -1) { + if ("Array Number String Boolean Date Object Error Symbol".indexOf(left.value) === -1) { if (left.value === "Math") { warning("W063", left); } else if (state.option.newcap) { @@ -19913,14 +21209,14 @@ var JSHINT = (function() { if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } } advance(")"); if (typeof left === "object") { - if (state.inES3() && left.value === "parseInt" && n === 1) { + if (!state.inES5() && left.value === "parseInt" && n === 1) { warning("W065", state.tokens.curr); } if (!state.option.evil) { @@ -19947,9 +21243,9 @@ var JSHINT = (function() { addInternalSrc(left, p[0].value); } } - if (!left.identifier && left.id !== "." && left.id !== "[" && - left.id !== "(" && left.id !== "&&" && left.id !== "||" && - left.id !== "?" && !(state.option.esnext && left["(name)"])) { + if (!left.identifier && left.id !== "." && left.id !== "[" && left.id !== "=>" && + left.id !== "(" && left.id !== "&&" && left.id !== "||" && left.id !== "?" && + !(state.inES6() && left["(name)"])) { warning("W067", that); } } @@ -20003,7 +21299,7 @@ var JSHINT = (function() { warning("W127"); } - comma(); + parseComma(); } } @@ -20048,7 +21344,13 @@ var JSHINT = (function() { // operator. (isFunctor(ret) && !isEndOfExpr()) || // Used as the return value of a single-statement arrow function - (ret.id === "{" && preceeding.id === "=>"); + (ret.id === "{" && preceeding.id === "=>") || + // Used to delineate an integer number literal from a dereferencing + // punctuator (otherwise interpreted as a decimal point) + (ret.type === "(number)" && + checkPunctuator(pn, ".") && /^\d+$/.test(ret.value)) || + // Used to wrap object destructuring assignment + (opening.beginsStmt && ret.id === "=" && ret.left.id === "{"); } } @@ -20056,7 +21358,7 @@ var JSHINT = (function() { // The operator may be necessary to override the default binding power of // neighboring operators (whenever there is an operator in use within the // first expression *or* the current group contains multiple expressions) - if (!isNecessary && (first.left || ret.exprs)) { + if (!isNecessary && (first.left || first.right || ret.exprs)) { isNecessary = (!isBeginOfExpr(preceeding) && first.lbp <= preceeding.lbp) || (!isEndOfExpr() && last.lbp < state.tokens.next.lbp); @@ -20078,7 +21380,7 @@ var JSHINT = (function() { var e = expression(10), s; if (e && e.type === "(string)") { if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { - if (isGlobalEval(left, state, funct)) { + if (isGlobalEval(left, state)) { warning("W061"); } } @@ -20105,7 +21407,7 @@ var JSHINT = (function() { function comprehensiveArrayExpression() { var res = {}; res.exps = true; - funct["(comparray)"].stack(); + state.funct["(comparray)"].stack(); // Handle reversed for expressions, used in spidermonkey var reversed = false; @@ -20114,7 +21416,7 @@ var JSHINT = (function() { if (!state.inMoz()) { warning("W116", state.tokens.next, "for", state.tokens.next.value); } - funct["(comparray)"].setState("use"); + state.funct["(comparray)"].setState("use"); res.right = expression(10); } @@ -20126,44 +21428,45 @@ var JSHINT = (function() { } } advance("("); - funct["(comparray)"].setState("define"); + state.funct["(comparray)"].setState("define"); res.left = expression(130); if (_.contains(["in", "of"], state.tokens.next.value)) { advance(); } else { error("E045", state.tokens.curr); } - funct["(comparray)"].setState("generate"); + state.funct["(comparray)"].setState("generate"); expression(10); advance(")"); if (state.tokens.next.value === "if") { advance("if"); advance("("); - funct["(comparray)"].setState("filter"); + state.funct["(comparray)"].setState("filter"); res.filter = expression(10); advance(")"); } if (!reversed) { - funct["(comparray)"].setState("use"); + state.funct["(comparray)"].setState("use"); res.right = expression(10); } advance("]"); - funct["(comparray)"].unstack(); + state.funct["(comparray)"].unstack(); return res; } prefix("[", function() { var blocktype = lookupBlockType(); if (blocktype.isCompArray) { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "array comprehension"); + if (!state.option.esnext && !state.inMoz()) { + warning("W118", state.tokens.curr, "array comprehension"); } return comprehensiveArrayExpression(); - } else if (blocktype.isDestAssign && !state.inESNext()) { - warning("W104", state.tokens.curr, "destructuring assignment"); + } else if (blocktype.isDestAssign) { + this.destructAssign = destructuringPattern({ openingParsed: true, assignment: true }); + return this; } var b = state.tokens.curr.line !== startLine(state.tokens.next); this.first = []; @@ -20197,8 +21500,8 @@ var JSHINT = (function() { this.first.push(expression(10)); if (state.tokens.next.id === ",") { - comma({ allowTrailing: true }); - if (state.tokens.next.id === "]" && !state.inES5(true)) { + parseComma({ allowTrailing: true }); + if (state.tokens.next.id === "]" && !state.inES5()) { warning("W070", state.tokens.curr); break; } @@ -20215,8 +21518,8 @@ var JSHINT = (function() { function isMethod() { - return funct["(statement)"] && funct["(statement)"].type === "class" || - funct["(context)"] && funct["(context)"]["(verb)"] === "class"; + return state.funct["(statement)"] && state.funct["(statement)"].type === "class" || + state.funct["(context)"] && state.funct["(context)"]["(verb)"] === "class"; } @@ -20266,20 +21569,22 @@ var JSHINT = (function() { * single-argument shorthand. * @param {bool} [options.parsedOpening] Whether the opening parenthesis has * already been parsed. + * @returns {{ arity: number, params: Array.}} */ function functionparams(options) { var next; - var params = []; + var paramsIds = []; var ident; var tokens = []; var t; var pastDefault = false; var pastRest = false; + var arity = 0; var loneArg = options && options.loneArg; if (loneArg && loneArg.identifier === true) { - addlabel(loneArg.value, { type: "unused", token: loneArg }); - return [loneArg.value]; + state.funct["(scope)"].addParam(loneArg.value, loneArg); + return { arity: 1, params: [ loneArg.value ] }; } next = state.tokens.next; @@ -20293,60 +21598,73 @@ var JSHINT = (function() { return; } + function addParam(addParamArgs) { + state.funct["(scope)"].addParam.apply(state.funct["(scope)"], addParamArgs); + } + for (;;) { + arity++; + // are added to the param scope + var currentParams = []; + if (_.contains(["{", "["], state.tokens.next.id)) { - tokens = destructuringExpression(); + tokens = destructuringPattern(); for (t in tokens) { t = tokens[t]; if (t.id) { - params.push(t.id); - addlabel(t.id, { type: "unused", token: t.token }); + paramsIds.push(t.id); + currentParams.push([t.id, t.token]); } } } else { - if (checkPunctuators(state.tokens.next, ["..."])) pastRest = true; + if (checkPunctuator(state.tokens.next, "...")) pastRest = true; ident = identifier(true); if (ident) { - params.push(ident); - addlabel(ident, { type: "unused", token: state.tokens.curr }); + paramsIds.push(ident); + currentParams.push([ident, state.tokens.curr]); } else { // Skip invalid parameter. while (!checkPunctuators(state.tokens.next, [",", ")"])) advance(); } } - // it is a syntax error to have a regular argument after a default argument + // It is valid to have a regular argument after a default argument + // since undefined can be used for missing parameters. Still warn as it is + // a possible code smell. if (pastDefault) { if (state.tokens.next.id !== "=") { - error("E051", state.tokens.current); + error("W138", state.tokens.current); } } if (state.tokens.next.id === "=") { - if (!state.inESNext()) { - warning("W119", state.tokens.next, "default parameters"); + if (!state.inES6()) { + warning("W119", state.tokens.next, "default parameters", "6"); } advance("="); pastDefault = true; expression(10); } + + // now we have evaluated the default expression, add the variable to the param scope + currentParams.forEach(addParam); + if (state.tokens.next.id === ",") { if (pastRest) { warning("W131", state.tokens.next); } - comma(); + parseComma(); } else { advance(")", next); - return params; + return { arity: arity, params: paramsIds }; } } } - function functor(name, token, scope, overwrites) { + function functor(name, token, overwrites) { var funct = { "(name)" : name, "(breakage)" : 0, "(loopage)" : 0, - "(scope)" : scope, "(tokens)" : {}, "(properties)": {}, @@ -20358,9 +21676,10 @@ var JSHINT = (function() { "(metrics)" : null, "(statement)" : null, "(context)" : null, - "(blockscope)": null, + "(scope)" : null, "(comparray)" : null, "(generator)" : null, + "(arrow)" : null, "(params)" : null }; @@ -20375,7 +21694,7 @@ var JSHINT = (function() { _.extend(funct, overwrites); if (funct["(context)"]) { - funct["(blockscope)"] = funct["(context)"]["(blockscope)"]; + funct["(scope)"] = funct["(context)"]["(scope)"]; funct["(comparray)"] = funct["(context)"]["(comparray)"]; } @@ -20450,10 +21769,9 @@ var JSHINT = (function() { * the body of member functions. */ function doFunction(options) { - var f, name, statement, classExprBinding, isGenerator, isArrow; + var f, token, name, statement, classExprBinding, isGenerator, isArrow, ignoreLoopFunc; var oldOption = state.option; var oldIgnored = state.ignored; - var oldScope = scope; if (options) { name = options.name; @@ -20461,37 +21779,53 @@ var JSHINT = (function() { classExprBinding = options.classExprBinding; isGenerator = options.type === "generator"; isArrow = options.type === "arrow"; + ignoreLoopFunc = options.ignoreLoopFunc; } state.option = Object.create(state.option); state.ignored = Object.create(state.ignored); - scope = Object.create(scope); - funct = functor(name || state.nameStack.infer(), state.tokens.next, scope, { + state.funct = functor(name || state.nameStack.infer(), state.tokens.next, { "(statement)": statement, - "(context)": funct, + "(context)": state.funct, + "(arrow)": isArrow, "(generator)": isGenerator }); - f = funct; - state.tokens.curr.funct = funct; + f = state.funct; + token = state.tokens.curr; + token.funct = state.funct; - functions.push(funct); + functions.push(state.funct); - if (name) { - addlabel(name, { type: "function" }); + // So that the function is available to itself and referencing itself is not + // seen as a closure, add the function name to a new scope, but do not + // test for unused (unused: false) + // it is a new block scope so that params can override it, it can be block scoped + // but declarations inside the function don't cause already declared error + state.funct["(scope)"].stack("functionouter"); + var internallyAccessibleName = name || classExprBinding; + if (internallyAccessibleName) { + state.funct["(scope)"].block.add(internallyAccessibleName, + classExprBinding ? "class" : "function", state.tokens.curr, false); } - if (classExprBinding) { - addlabel(classExprBinding, { type: "function" }); - } + // create the param scope (params added in functionparams) + state.funct["(scope)"].stack("functionparams"); - funct["(params)"] = functionparams(options); - funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); + var paramsInfo = functionparams(options); + + if (paramsInfo) { + state.funct["(params)"] = paramsInfo.params; + state.funct["(metrics)"].arity = paramsInfo.arity; + state.funct["(metrics)"].verifyMaxParametersPerFunction(); + } else { + state.funct["(metrics)"].arity = 0; + } if (isArrow) { - if (!state.option.esnext) { - warning("W119", state.tokens.curr, "arrow function syntax (=>)"); + if (!state.inES6(true)) { + warning("W119", state.tokens.curr, "arrow function syntax (=>)", "6"); } if (!options.loneArg) { @@ -20502,26 +21836,34 @@ var JSHINT = (function() { block(false, true, true, isArrow); if (!state.option.noyield && isGenerator && - funct["(generator)"] !== "yielded") { + state.funct["(generator)"] !== "yielded") { warning("W124", state.tokens.curr); } - funct["(metrics)"].verifyMaxStatementsPerFunction(); - funct["(metrics)"].verifyMaxComplexityPerFunction(); - funct["(unusedOption)"] = state.option.unused; - - scope = oldScope; + state.funct["(metrics)"].verifyMaxStatementsPerFunction(); + state.funct["(metrics)"].verifyMaxComplexityPerFunction(); + state.funct["(unusedOption)"] = state.option.unused; state.option = oldOption; state.ignored = oldIgnored; - funct["(last)"] = state.tokens.curr.line; - funct["(lastcharacter)"] = state.tokens.curr.character; + state.funct["(last)"] = state.tokens.curr.line; + state.funct["(lastcharacter)"] = state.tokens.curr.character; - _.map(Object.keys(funct), function(key) { - if (key[0] === "(") return; - funct["(blockscope)"].unshadow(key); - }); + // unstack the params scope + state.funct["(scope)"].unstack(); // also does usage and label checks - funct = funct["(context)"]; + // unstack the function outer stack + state.funct["(scope)"].unstack(); + + state.funct = state.funct["(context)"]; + + if (!ignoreLoopFunc && !state.option.loopfunc && state.funct["(loopage)"]) { + // If the function we just parsed accesses any non-local variables + // trigger a warning. Otherwise, the function is safe even within + // a loop. + if (f["(isCapturing)"]) { + warning("W083", token); + } + } return f; } @@ -20531,6 +21873,7 @@ var JSHINT = (function() { statementCount: 0, nestedBlockDepth: -1, ComplexityCount: 1, + arity: 0, verifyMaxStatementsPerFunction: function() { if (state.option.maxstatements && @@ -20539,11 +21882,10 @@ var JSHINT = (function() { } }, - verifyMaxParametersPerFunction: function(params) { - params = params || []; - - if (_.isNumber(state.option.maxparams) && params.length > state.option.maxparams) { - warning("W072", functionStartToken, params.length); + verifyMaxParametersPerFunction: function() { + if (_.isNumber(state.option.maxparams) && + this.arity > state.option.maxparams) { + warning("W072", functionStartToken, this.arity); } }, @@ -20566,7 +21908,7 @@ var JSHINT = (function() { } function increaseComplexityCount() { - funct["(metrics)"].ComplexityCount += 1; + state.funct["(metrics)"].ComplexityCount += 1; } // Parse assignments that were found instead of conditionals. @@ -20606,17 +21948,32 @@ var JSHINT = (function() { // Check for lonely setters if in the ES5 mode. if (state.inES5()) { for (var name in props) { - if (_.has(props, name) && props[name].setterToken && !props[name].getterToken) { + if (props[name] && props[name].setterToken && !props[name].getterToken) { warning("W078", props[name].setterToken); } } } } + function metaProperty(name, c) { + if (checkPunctuator(state.tokens.next, ".")) { + var left = state.tokens.curr.id; + advance("."); + var id = identifier(); + state.tokens.curr.isMetaProperty = true; + if (name !== id) { + error("E057", state.tokens.prev, left, id); + } else { + c(); + } + return state.tokens.curr; + } + } + (function(x) { x.nud = function() { var b, f, i, p, t, isGeneratorMethod = false, nextVal; - var props = {}; // All properties, including accessors + var props = Object.create(null); // All properties, including accessors b = state.tokens.curr.line !== startLine(state.tokens.next); if (b) { @@ -20626,6 +21983,12 @@ var JSHINT = (function() { } } + var blocktype = lookupBlockType(); + if (blocktype.isDestAssign) { + this.destructAssign = destructuringPattern({ openingParsed: true, assignment: true }); + return this; + } + for (;;) { if (state.tokens.next.id === "}") { break; @@ -20634,8 +21997,8 @@ var JSHINT = (function() { nextVal = state.tokens.next.value; if (state.tokens.next.identifier && (peekIgnoreEOL().id === "," || peekIgnoreEOL().id === "}")) { - if (!state.inESNext()) { - warning("W104", state.tokens.next, "object short notation"); + if (!state.inES6()) { + warning("W104", state.tokens.next, "object short notation", "6"); } i = propertyName(true); saveProperty(props, i, state.tokens.next); @@ -20653,8 +22016,8 @@ var JSHINT = (function() { // ES6 allows for get() {...} and set() {...} method // definition shorthand syntax, so we don't produce an error - // if the esnext option is enabled. - if (!i && !state.inESNext()) { + // if linting ECMAScript 6 code. + if (!i && !state.inES6()) { error("E035"); } @@ -20671,13 +22034,13 @@ var JSHINT = (function() { // Don't warn about getter/setter pairs if this is an ES6 concise method if (nextVal === "get" && i && p) { warning("W076", t, p[0], i); - } else if (nextVal === "set" && i && (!p || p.length !== 1)) { + } else if (nextVal === "set" && i && f["(metrics)"].arity !== 1) { warning("W077", t, i); } } else { if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { - if (!state.inESNext()) { - warning("W104", state.tokens.next, "generator functions"); + if (!state.inES6()) { + warning("W104", state.tokens.next, "generator functions", "6"); } advance("*"); isGeneratorMethod = true; @@ -20699,8 +22062,8 @@ var JSHINT = (function() { } if (state.tokens.next.value === "(") { - if (!state.inESNext()) { - warning("W104", state.tokens.curr, "concise methods"); + if (!state.inES6()) { + warning("W104", state.tokens.curr, "concise methods", "6"); } doFunction({ type: isGeneratorMethod ? "generator" : null }); } else { @@ -20712,10 +22075,10 @@ var JSHINT = (function() { countMember(i); if (state.tokens.next.id === ",") { - comma({ allowTrailing: true, property: true }); + parseComma({ allowTrailing: true, property: true }); if (state.tokens.next.id === ",") { warning("W070", state.tokens.curr); - } else if (state.tokens.next.id === "}" && !state.inES5(true)) { + } else if (state.tokens.next.id === "}" && !state.inES5()) { warning("W070", state.tokens.curr); } } else { @@ -20736,86 +22099,156 @@ var JSHINT = (function() { }; }(delim("{"))); - function destructuringExpression() { - var id, ids; - var identifiers = []; - if (!state.inESNext()) { - warning("W104", state.tokens.curr, "destructuring expression"); + function destructuringPattern(options) { + var isAssignment = options && options.assignment; + + if (!state.inES6()) { + warning("W104", state.tokens.curr, + isAssignment ? "destructuring assignment" : "destructuring binding", "6"); } + + return destructuringPatternRecursive(options); + } + + function destructuringPatternRecursive(options) { + var ids; + var identifiers = []; + var openingParsed = options && options.openingParsed; + var isAssignment = options && options.assignment; + var recursiveOptions = isAssignment ? { assignment: isAssignment } : null; + var firstToken = openingParsed ? state.tokens.curr : state.tokens.next; + var nextInnerDE = function() { var ident; if (checkPunctuators(state.tokens.next, ["[", "{"])) { - ids = destructuringExpression(); + ids = destructuringPatternRecursive(recursiveOptions); for (var id in ids) { id = ids[id]; identifiers.push({ id: id.id, token: id.token }); } - } else if (checkPunctuators(state.tokens.next, [","])) { + } else if (checkPunctuator(state.tokens.next, ",")) { identifiers.push({ id: null, token: state.tokens.curr }); - } else if (checkPunctuators(state.tokens.next, ["("])) { + } else if (checkPunctuator(state.tokens.next, "(")) { advance("("); nextInnerDE(); advance(")"); } else { - var is_rest = checkPunctuators(state.tokens.next, ["..."]); - ident = identifier(); - if (ident) + var is_rest = checkPunctuator(state.tokens.next, "..."); + + if (isAssignment) { + var assignTarget = expression(20); + if (assignTarget) { + checkLeftSideAssign(assignTarget); + + // if the target was a simple identifier, add it to the list to return + if (assignTarget.identifier) { + ident = assignTarget.value; + } + } + } else { + ident = identifier(); + } + if (ident) { identifiers.push({ id: ident, token: state.tokens.curr }); + } return is_rest; } return false; }; - if (checkPunctuators(state.tokens.next, ["["])) { - advance("["); - var element_after_rest = false; - if (nextInnerDE() && checkPunctuators(state.tokens.next, [","]) && - !element_after_rest) { - warning("W130", state.tokens.next); - element_after_rest = true; - } - while (!checkPunctuators(state.tokens.next, ["]"])) { - advance(","); - if (checkPunctuators(state.tokens.next, ["]"])) { - // Trailing comma - break; - } - if (nextInnerDE() && checkPunctuators(state.tokens.next, [","]) && - !element_after_rest) { - warning("W130", state.tokens.next); - element_after_rest = true; - } - } - advance("]"); - } else if (checkPunctuators(state.tokens.next, ["{"])) { - advance("{"); - id = identifier(); - if (checkPunctuators(state.tokens.next, [":"])) { + var assignmentProperty = function() { + var id; + if (checkPunctuator(state.tokens.next, "[")) { + advance("["); + expression(10); + advance("]"); + advance(":"); + nextInnerDE(); + } else if (state.tokens.next.id === "(string)" || + state.tokens.next.id === "(number)") { + advance(); advance(":"); nextInnerDE(); } else { - identifiers.push({ id: id, token: state.tokens.curr }); - } - while (!checkPunctuators(state.tokens.next, ["}"])) { - advance(","); - if (checkPunctuators(state.tokens.next, ["}"])) { - // Trailing comma - // ObjectBindingPattern: { BindingPropertyList , } - break; - } + // this id will either be the property name or the property name and the assigning identifier id = identifier(); - if (checkPunctuators(state.tokens.next, [":"])) { + if (checkPunctuator(state.tokens.next, ":")) { advance(":"); nextInnerDE(); - } else { + } else if (id) { + // in this case we are assigning (not declaring), so check assignment + if (isAssignment) { + checkLeftSideAssign(state.tokens.curr); + } identifiers.push({ id: id, token: state.tokens.curr }); } } + }; + + var id, value; + if (checkPunctuator(firstToken, "[")) { + if (!openingParsed) { + advance("["); + } + if (checkPunctuator(state.tokens.next, "]")) { + warning("W137", state.tokens.curr); + } + var element_after_rest = false; + while (!checkPunctuator(state.tokens.next, "]")) { + if (nextInnerDE() && !element_after_rest && + checkPunctuator(state.tokens.next, ",")) { + warning("W130", state.tokens.next); + element_after_rest = true; + } + if (checkPunctuator(state.tokens.next, "=")) { + if (checkPunctuator(state.tokens.prev, "...")) { + advance("]"); + } else { + advance("="); + } + id = state.tokens.prev; + value = expression(10); + if (value && value.type === "undefined") { + warning("W080", id, id.value); + } + } + if (!checkPunctuator(state.tokens.next, "]")) { + advance(","); + } + } + advance("]"); + } else if (checkPunctuator(firstToken, "{")) { + + if (!openingParsed) { + advance("{"); + } + if (checkPunctuator(state.tokens.next, "}")) { + warning("W137", state.tokens.curr); + } + while (!checkPunctuator(state.tokens.next, "}")) { + assignmentProperty(); + if (checkPunctuator(state.tokens.next, "=")) { + advance("="); + id = state.tokens.prev; + value = expression(10); + if (value && value.type === "undefined") { + warning("W080", id, id.value); + } + } + if (!checkPunctuator(state.tokens.next, "}")) { + advance(","); + if (checkPunctuator(state.tokens.next, "}")) { + // Trailing comma + // ObjectBindingPattern: { BindingPropertyList , } + break; + } + } + } advance("}"); } return identifiers; } - function destructuringExpressionMatch(tokens, value) { + function destructuringPatternMatch(tokens, value) { var first = value.first; if (!first) @@ -20841,8 +22274,8 @@ var JSHINT = (function() { var isConst = type === "const"; var tokens, lone, value, letblock; - if (!state.inESNext()) { - warning("W104", state.tokens.curr, type); + if (!state.inES6()) { + warning("W104", state.tokens.curr, type, "6"); } if (isLet && state.tokens.next.value === "(") { @@ -20850,9 +22283,9 @@ var JSHINT = (function() { warning("W118", state.tokens.next, "let block"); } advance("("); - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); letblock = true; - } else if (funct["(noblockscopedvar)"]) { + } else if (state.funct["(noblockscopedvar)"]) { error("E048", state.tokens.curr, isConst ? "Const" : "Let"); } @@ -20860,73 +22293,68 @@ var JSHINT = (function() { for (;;) { var names = []; if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); + tokens = destructuringPattern(); lone = false; } else { tokens = [ { id: identifier(), token: state.tokens.curr } ]; lone = true; - if (inexport) { - exported[state.tokens.curr.value] = true; - state.tokens.curr.exported = true; - } } - for (var t in tokens) { - if (tokens.hasOwnProperty(t)) { - t = tokens[t]; - if (state.inESNext()) { - // only look in the latest scope because we can shadow - if (funct["(blockscope)"].current.labeltype(t.id) === "const") { - warning("E011", null, t.id); - } - } - if (funct["(global)"]) { - if (predefined[t.id] === false) { - warning("W079", t.token, t.id); - } - } - if (t.id && !funct["(noblockscopedvar)"]) { - addlabel(t.id, { - type: isConst ? "const" : "unused", - token: t.token, - isblockscoped: true }); - names.push(t.token); - } - } - } - - statement.first = statement.first.concat(names); if (!prefix && isConst && state.tokens.next.id !== "=") { warning("E012", state.tokens.curr, state.tokens.curr.value); } + for (var t in tokens) { + if (tokens.hasOwnProperty(t)) { + t = tokens[t]; + if (state.funct["(scope)"].block.isGlobal()) { + if (predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + } + if (t.id && !state.funct["(noblockscopedvar)"]) { + state.funct["(scope)"].addlabel(t.id, { + type: type, + token: t.token }); + names.push(t.token); + + if (lone && inexport) { + state.funct["(scope)"].setExported(t.token.value, t.token); + } + } + } + } + if (state.tokens.next.id === "=") { advance("="); - if (!prefix && state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } if (!prefix && peek(0).id === "=" && state.tokens.next.identifier) { warning("W120", state.tokens.next, state.tokens.next.value); } + var id = state.tokens.prev; // don't accept `in` in expression if prefix is used for ForIn/Of loop. value = expression(prefix ? 120 : 10); + if (!prefix && value && value.type === "undefined") { + warning("W080", id, id.value); + } if (lone) { tokens[0].first = value; } else { - destructuringExpressionMatch(names, value); + destructuringPatternMatch(names, value); } } + statement.first = statement.first.concat(names); + if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } if (letblock) { advance(")"); block(true, true); statement.block = true; - funct["(blockscope)"].unstack(); + state.funct["(scope)"].unstack(); } return statement; @@ -20955,90 +22383,81 @@ var JSHINT = (function() { for (;;) { var names = []; if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); + tokens = destructuringPattern(); lone = false; } else { tokens = [ { id: identifier(), token: state.tokens.curr } ]; lone = true; - if (inexport) { - exported[state.tokens.curr.value] = true; - state.tokens.curr.exported = true; - } } + + if (!(prefix && implied) && report && state.option.varstmt) { + warning("W132", this); + } + + this.first = this.first.concat(names); + for (var t in tokens) { if (tokens.hasOwnProperty(t)) { t = tokens[t]; - if (state.inESNext()) { - // because var is function scoped, look in the whole function - if (funct["(blockscope)"].labeltype(t.id) === "const") { - warning("E011", null, t.id); - } - } - if (!implied && funct["(global)"]) { + if (!implied && state.funct["(global)"] && !state.impliedClosure()) { if (predefined[t.id] === false) { warning("W079", t.token, t.id); } else if (state.option.futurehostile === false) { if ((!state.inES5() && vars.ecmaIdentifiers[5][t.id] === false) || - (!state.inESNext() && vars.ecmaIdentifiers[6][t.id] === false)) { + (!state.inES6() && vars.ecmaIdentifiers[6][t.id] === false)) { warning("W129", t.token, t.id); } } } if (t.id) { if (implied === "for") { - var ident = t.token.value; - switch (funct[ident]) { - case "unused": - funct[ident] = "var"; - break; - case "var": - break; - default: - if (!funct["(blockscope)"].getlabel(ident) && - !(scope[ident] || {})[ident]) { - if (report) warning("W088", t.token, ident); - } + + if (!state.funct["(scope)"].has(t.id)) { + if (report) warning("W088", t.token, t.id); } + state.funct["(scope)"].block.use(t.id, t.token); } else { - addlabel(t.id, { type: "unused", token: t.token }); + state.funct["(scope)"].addlabel(t.id, { + type: "var", + token: t.token }); + + if (lone && inexport) { + state.funct["(scope)"].setExported(t.id, t.token); + } } names.push(t.token); } } } - if (!prefix && report && state.option.varstmt) { - warning("W132", this); - } - - this.first = this.first.concat(names); - if (state.tokens.next.id === "=") { state.nameStack.set(state.tokens.curr); advance("="); - if (!prefix && report && state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } if (peek(0).id === "=" && state.tokens.next.identifier) { if (!prefix && report && - !funct["(params)"] || funct["(params)"].indexOf(state.tokens.next.value) === -1) { + !state.funct["(params)"] || + state.funct["(params)"].indexOf(state.tokens.next.value) === -1) { warning("W120", state.tokens.next, state.tokens.next.value); } } + var id = state.tokens.prev; // don't accept `in` in expression if prefix is used for ForIn/Of loop. value = expression(prefix ? 120 : 10); + if (value && !prefix && report && !state.funct["(loopage)"] && value.type === "undefined") { + warning("W080", id, id.value); + } if (lone) { tokens[0].first = value; } else { - destructuringExpressionMatch(names, value); + destructuringPatternMatch(names, value); } } if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } return this; @@ -21052,13 +22471,16 @@ var JSHINT = (function() { function classdef(isStatement) { /*jshint validthis:true */ - if (!state.inESNext()) { - warning("W104", state.tokens.curr, "class"); + if (!state.inES6()) { + warning("W104", state.tokens.curr, "class", "6"); } if (isStatement) { // BindingIdentifier this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); + + state.funct["(scope)"].addlabel(this.name, { + type: "class", + token: state.tokens.curr }); } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { // BindingIdentifier(opt) this.name = identifier(); @@ -21091,8 +22513,8 @@ var JSHINT = (function() { var isStatic; var isGenerator; var getset; - var props = {}; - var staticProps = {}; + var props = Object.create(null); + var staticProps = Object.create(null); var computed; for (var i = 0; state.tokens.next.id !== "}"; ++i) { name = state.tokens.next; @@ -21122,7 +22544,7 @@ var JSHINT = (function() { advance(); computed = false; if (name.identifier && name.value === "static") { - if (checkPunctuators(state.tokens.next, ["*"])) { + if (checkPunctuator(state.tokens.next, "*")) { isGenerator = true; advance("*"); } @@ -21152,11 +22574,11 @@ var JSHINT = (function() { continue; } - if (!checkPunctuators(state.tokens.next, ["("])) { + if (!checkPunctuator(state.tokens.next, "(")) { // error --- class properties must be methods error("E054", state.tokens.next, state.tokens.next.value); while (state.tokens.next.id !== "}" && - !checkPunctuators(state.tokens.next, ["("])) { + !checkPunctuator(state.tokens.next, "(")) { advance(); } if (state.tokens.next.value !== "(") { @@ -21198,37 +22620,37 @@ var JSHINT = (function() { checkProperties(props); } - blockstmt("function", function() { + blockstmt("function", function(context) { + var inexport = context && context.inexport; var generator = false; if (state.tokens.next.value === "*") { advance("*"); - if (state.inESNext({ strict: true })) { + if (state.inES6(true)) { generator = true; } else { - warning("W119", state.tokens.curr, "function*"); + warning("W119", state.tokens.curr, "function*", "6"); } } if (inblock) { warning("W082", state.tokens.curr); - } var i = optionalidentifier(); + state.funct["(scope)"].addlabel(i, { + type: "function", + token: state.tokens.curr }); + if (i === undefined) { warning("W025"); + } else if (inexport) { + state.funct["(scope)"].setExported(i, state.tokens.prev); } - // check if a identifier with the same name is already defined - // in the blockscope as a const - if (funct["(blockscope)"].labeltype(i) === "const") { - warning("E011", null, i); - } - addlabel(i, { type: "unction", token: state.tokens.curr }); - doFunction({ name: i, statement: this, - type: generator ? "generator" : null + type: generator ? "generator" : null, + ignoreLoopFunc: inblock // a declaration may already have warned }); if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { error("E039"); @@ -21240,27 +22662,15 @@ var JSHINT = (function() { var generator = false; if (state.tokens.next.value === "*") { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "function*"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "function*", "6"); } advance("*"); generator = true; } var i = optionalidentifier(); - var fn = doFunction({ name: i, type: generator ? "generator" : null }); - - function isVariable(name) { return name[0] !== "("; } - function isLocal(name) { return fn[name] === "var"; } - - if (!state.option.loopfunc && funct["(loopage)"]) { - // If the function we just parsed accesses any non-local variables - // trigger a warning. Otherwise, the function is safe even within - // a loop. - if (_.some(fn, function(val, name) { return isVariable(name) && !isLocal(name); })) { - warning("W083"); - } - } + doFunction({ name: i, type: generator ? "generator" : null }); return this; }); @@ -21292,7 +22702,7 @@ var JSHINT = (function() { // When the if is within a for-in loop and the condition has a negative form, // check if the body contains nothing but a continue statement if (forinifcheck && forinifcheck.type === "(negative)") { - if (s && s.length === 1 && s[0].type === "(identifier)" && s[0].value === "continue") { + if (s && s[0] && s[0].type === "(identifier)" && s[0].value === "continue") { forinifcheck.type = "(negative-with-continue)"; } } @@ -21312,32 +22722,23 @@ var JSHINT = (function() { var b; function doCatch() { - var oldScope = scope; - var e; - advance("catch"); advance("("); - scope = Object.create(oldScope); + state.funct["(scope)"].stack("catchparams"); - e = state.tokens.next.value; - if (state.tokens.next.type !== "(identifier)") { - e = null; - warning("E030", state.tokens.next, e); - } - - advance(); - - funct = functor("(catch)", state.tokens.next, scope, { - "(context)" : funct, - "(breakage)" : funct["(breakage)"], - "(loopage)" : funct["(loopage)"], - "(statement)": false, - "(catch)" : true - }); - - if (e) { - addlabel(e, { type: "exception" }); + if (checkPunctuators(state.tokens.next, ["[", "{"])) { + var tokens = destructuringPattern(); + _.each(tokens, function(token) { + if (token.id) { + state.funct["(scope)"].addParam(token.id, token, "exception"); + } + }); + } else if (state.tokens.next.type !== "(identifier)") { + warning("E030", state.tokens.next, state.tokens.next.value); + } else { + // only advance if we have an identifier so we can continue parsing in the most common error - that no param is given. + state.funct["(scope)"].addParam(identifier(), state.tokens.curr, "exception"); } if (state.tokens.next.value === "if") { @@ -21350,16 +22751,9 @@ var JSHINT = (function() { advance(")"); - state.tokens.curr.funct = funct; - functions.push(funct); - block(false); - scope = oldScope; - - funct["(last)"] = state.tokens.curr.line; - funct["(lastcharacter)"] = state.tokens.curr.character; - funct = funct["(context)"]; + state.funct["(scope)"].unstack(); } block(true); @@ -21388,15 +22782,15 @@ var JSHINT = (function() { blockstmt("while", function() { var t = state.tokens.next; - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); checkCondAssignment(expression(0)); advance(")", t); block(true, true); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; return this; }).labelled = true; @@ -21421,7 +22815,7 @@ var JSHINT = (function() { var g = false; var noindent = false; - funct["(breakage)"] += 1; + state.funct["(breakage)"] += 1; advance("("); checkCondAssignment(expression(0)); advance(")", t); @@ -21439,7 +22833,7 @@ var JSHINT = (function() { for (;;) { switch (state.tokens.next.id) { case "case": - switch (funct["(verb)"]) { + switch (state.funct["(verb)"]) { case "yield": case "break": case "case": @@ -21452,7 +22846,7 @@ var JSHINT = (function() { // You can tell JSHint that you don't use break intentionally by // adding a comment /* falls through */ on a line just before // the next `case`. - if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + if (!state.tokens.curr.caseFallsThrough) { warning("W086", state.tokens.curr, "case"); } } @@ -21462,10 +22856,10 @@ var JSHINT = (function() { increaseComplexityCount(); g = true; advance(":"); - funct["(verb)"] = "case"; + state.funct["(verb)"] = "case"; break; case "default": - switch (funct["(verb)"]) { + switch (state.funct["(verb)"]) { case "yield": case "break": case "continue": @@ -21476,7 +22870,7 @@ var JSHINT = (function() { // Do not display a warning if 'default' is the first statement or if // there is a special /* falls through */ comment. if (this.cases.length) { - if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + if (!state.tokens.curr.caseFallsThrough) { warning("W086", state.tokens.curr, "default"); } } @@ -21491,8 +22885,8 @@ var JSHINT = (function() { indent -= state.option.indent; advance("}", t); - funct["(breakage)"] -= 1; - funct["(verb)"] = undefined; + state.funct["(breakage)"] -= 1; + state.funct["(verb)"] = undefined; return; case "(end)": error("E023", state.tokens.next, "}"); @@ -21525,6 +22919,7 @@ var JSHINT = (function() { indent -= state.option.indent; } } + return this; }).labelled = true; stmt("debugger", function() { @@ -21536,8 +22931,8 @@ var JSHINT = (function() { (function() { var x = stmt("do", function() { - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; increaseComplexityCount(); this.first = block(true, true); @@ -21546,8 +22941,8 @@ var JSHINT = (function() { advance("("); checkCondAssignment(expression(0)); advance(")", t); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; return this; }); x.labelled = true; @@ -21567,8 +22962,6 @@ var JSHINT = (function() { } } - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); @@ -21589,16 +22982,16 @@ var JSHINT = (function() { else if (checkPunctuators(nextop, ["}", "]"])) --level; if (level < 0) break; if (level === 0) { - if (!comma && checkPunctuators(nextop, [","])) comma = nextop; - else if (!initializer && checkPunctuators(nextop, ["="])) initializer = nextop; + if (!comma && checkPunctuator(nextop, ",")) comma = nextop; + else if (!initializer && checkPunctuator(nextop, "=")) initializer = nextop; } } while (level > 0 || !_.contains(inof, nextop.value) && nextop.value !== ";" && nextop.type !== "(end)"); // Is this a JSCS bug? This looks really weird. // if we're in a for (… in|of …) statement if (_.contains(inof, nextop.value)) { - if (!state.inESNext() && nextop.value === "of") { - error("W104", nextop, "for of"); + if (!state.inES6() && nextop.value === "of") { + warning("W104", nextop, "for of", "6"); } var ok = !(initializer || comma); @@ -21617,7 +23010,7 @@ var JSHINT = (function() { advance(state.tokens.next.id); // create a new block scope letscope = true; - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); state.tokens.curr.fud({ prefix: true }); } else { // Parse as a var statement, with implied bindings. Ignore errors if an error @@ -21642,6 +23035,9 @@ var JSHINT = (function() { }); } + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; + s = block(true, true); if (nextop.value === "in" && state.option.forin) { @@ -21662,8 +23058,8 @@ var JSHINT = (function() { state.forinifcheckneeded = false; } - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; } else { if (foreachtok) { error("E045", foreachtok); @@ -21676,7 +23072,7 @@ var JSHINT = (function() { advance("let"); // create a new block scope letscope = true; - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); state.tokens.curr.fud(); } else { for (;;) { @@ -21684,12 +23080,16 @@ var JSHINT = (function() { if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } } } nolinebreak(state.tokens.curr); advance(";"); + + // start loopage after the first ; as the next two expressions are executed + // on every loop + state.funct["(loopage)"] += 1; if (state.tokens.next.id !== ";") { checkCondAssignment(expression(0)); } @@ -21704,18 +23104,19 @@ var JSHINT = (function() { if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } } advance(")", t); + state.funct["(breakage)"] += 1; block(true, true); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; } // unstack loop blockscope if (letscope) { - funct["(blockscope)"].unstack(); + state.funct["(scope)"].unstack(); } return this; }).labelled = true; @@ -21724,22 +23125,19 @@ var JSHINT = (function() { stmt("break", function() { var v = state.tokens.next.value; - if (funct["(breakage)"] === 0) - warning("W052", state.tokens.next, this.value); - if (!state.option.asi) nolinebreak(this); - if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { - if (state.tokens.curr.line === startLine(state.tokens.next)) { - if (funct[v] !== "label") { - warning("W090", state.tokens.next, v); - } else if (scope[v] !== funct) { - warning("W091", state.tokens.next, v); - } - this.first = state.tokens.next; - advance(); + if (state.tokens.next.id !== ";" && !state.tokens.next.reach && + state.tokens.curr.line === startLine(state.tokens.next)) { + if (!state.funct["(scope)"].funct.hasBreakLabel(v)) { + warning("W090", state.tokens.next, v); } + this.first = state.tokens.next; + advance(); + } else { + if (state.funct["(breakage)"] === 0) + warning("W052", state.tokens.next, this.value); } reachable(this); @@ -21751,7 +23149,9 @@ var JSHINT = (function() { stmt("continue", function() { var v = state.tokens.next.value; - if (funct["(breakage)"] === 0) + if (state.funct["(breakage)"] === 0) + warning("W052", state.tokens.next, this.value); + if (!state.funct["(loopage)"]) warning("W052", state.tokens.next, this.value); if (!state.option.asi) @@ -21759,16 +23159,12 @@ var JSHINT = (function() { if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { if (state.tokens.curr.line === startLine(state.tokens.next)) { - if (funct[v] !== "label") { + if (!state.funct["(scope)"].funct.hasBreakLabel(v)) { warning("W090", state.tokens.next, v); - } else if (scope[v] !== funct) { - warning("W091", state.tokens.next, v); } this.first = state.tokens.next; advance(); } - } else if (!funct["(loopage)"]) { - warning("W052", state.tokens.next, this.value); } reachable(this); @@ -21805,15 +23201,15 @@ var JSHINT = (function() { x.lbp = 25; }(prefix("yield", function() { var prev = state.tokens.prev; - if (state.inESNext(true) && !funct["(generator)"]) { + if (state.inES6(true) && !state.funct["(generator)"]) { // If it's a yield within a catch clause inside a generator then that's ok - if (!("(catch)" === funct["(name)"] && funct["(context)"]["(generator)"])) { + if (!("(catch)" === state.funct["(name)"] && state.funct["(context)"]["(generator)"])) { error("E046", state.tokens.curr, "yield"); } - } else if (!state.inESNext()) { - warning("W104", state.tokens.curr, "yield"); + } else if (!state.inES6()) { + warning("W104", state.tokens.curr, "yield", "6"); } - funct["(generator)"] = "yielded"; + state.funct["(generator)"] = "yielded"; var delegatingYield = false; if (state.tokens.next.value === "*") { @@ -21823,7 +23219,8 @@ var JSHINT = (function() { if (this.line === startLine(state.tokens.next) || !state.inMoz()) { if (delegatingYield || - (state.tokens.next.id !== ";" && !state.tokens.next.reach && state.tokens.next.nud)) { + (state.tokens.next.id !== ";" && !state.option.asi && + !state.tokens.next.reach && state.tokens.next.nud)) { nobreaknonadjacent(state.tokens.curr, state.tokens.next); this.first = expression(10); @@ -21855,8 +23252,8 @@ var JSHINT = (function() { }).exps = true; stmt("import", function() { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "import"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "import", "6"); } if (state.tokens.next.type === "(string)") { @@ -21868,7 +23265,11 @@ var JSHINT = (function() { if (state.tokens.next.identifier) { // ImportClause :: ImportedDefaultBinding this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); + // Import bindings are immutable (see ES6 8.1.1.5.5) + state.funct["(scope)"].addlabel(this.name, { + type: "const", + token: state.tokens.curr }); + if (state.tokens.next.value === ",") { // ImportClause :: ImportedDefaultBinding , NameSpaceImport // ImportClause :: ImportedDefaultBinding , NamedImports @@ -21890,7 +23291,10 @@ var JSHINT = (function() { advance("as"); if (state.tokens.next.identifier) { this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); + // Import bindings are immutable (see ES6 8.1.1.5.5) + state.funct["(scope)"].addlabel(this.name, { + type: "const", + token: state.tokens.curr }); } } else { // ImportClause :: NamedImports @@ -21911,7 +23315,11 @@ var JSHINT = (function() { advance("as"); importName = identifier(); } - addlabel(importName, { type: "unused", token: state.tokens.curr }); + + // Import bindings are immutable (see ES6 8.1.1.5.5) + state.funct["(scope)"].addlabel(importName, { + type: "const", + token: state.tokens.curr }); if (state.tokens.next.value === ",") { advance(","); @@ -21936,12 +23344,12 @@ var JSHINT = (function() { var token; var identifier; - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "export"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "export", "6"); ok = false; } - if (!funct["(global)"] || !funct["(blockscope)"].atTop()) { + if (!state.funct["(scope)"].block.isGlobal()) { error("E053", state.tokens.curr); ok = false; } @@ -21955,11 +23363,14 @@ var JSHINT = (function() { } if (state.tokens.next.type === "default") { - // ExportDeclaration :: export default HoistableDeclaration - // ExportDeclaration :: export default ClassDeclaration + // ExportDeclaration :: + // export default [lookahead  { function, class }] AssignmentExpression[In] ; + // export default HoistableDeclaration + // export default ClassDeclaration state.nameStack.set(state.tokens.next); advance("default"); - if (state.tokens.next.id === "function" || state.tokens.next.id === "class") { + var exportType = state.tokens.next.id; + if (exportType === "function" || exportType === "class") { this.block = true; } @@ -21967,15 +23378,15 @@ var JSHINT = (function() { expression(10); - if (state.tokens.next.id === "class") { - identifier = token.name; - } else { - identifier = token.value; - } + identifier = token.value; - addlabel(identifier, { - type: "function", token: token - }); + if (this.block) { + state.funct["(scope)"].addlabel(identifier, { + type: exportType, + token: token }); + + state.funct["(scope)"].setExported(identifier, token); + } return this; } @@ -21990,7 +23401,6 @@ var JSHINT = (function() { } advance(); - state.tokens.curr.exported = ok; exportedTokens.push(state.tokens.curr); if (state.tokens.next.value === "as") { @@ -22017,11 +23427,7 @@ var JSHINT = (function() { advance("(string)"); } else if (ok) { exportedTokens.forEach(function(token) { - if (!funct[token.value]) { - isundef(funct, "W117", token, token.value); - } - exported[token.value] = true; - funct["(blockscope)"].setExported(token.value); + state.funct["(scope)"].setExported(token.value, token); }); } return this; @@ -22043,16 +23449,14 @@ var JSHINT = (function() { // ExportDeclaration :: export Declaration this.block = true; advance("function"); - exported[state.tokens.next.value] = ok; - state.tokens.next.exported = true; - state.syntax["function"].fud(); + state.syntax["function"].fud({ inexport:true }); } else if (state.tokens.next.id === "class") { // ExportDeclaration :: export Declaration this.block = true; advance("class"); - exported[state.tokens.next.value] = ok; - state.tokens.next.exported = true; + var classNameToken = state.tokens.next; state.syntax["class"].fud(); + state.funct["(scope)"].setExported(classNameToken.value, classNameToken); } else { error("E024", state.tokens.next, state.tokens.next.value); } @@ -22063,6 +23467,7 @@ var JSHINT = (function() { // Future Reserved Words FutureReservedWord("abstract"); + FutureReservedWord("await", { es5: true, moduleOnly: true }); FutureReservedWord("boolean"); FutureReservedWord("byte"); FutureReservedWord("char"); @@ -22095,14 +23500,16 @@ var JSHINT = (function() { // expression is a comprehension array, destructuring assignment or a json value. var lookupBlockType = function() { - var pn, pn1; + var pn, pn1, prev; var i = -1; var bracketStack = 0; var ret = {}; - if (checkPunctuators(state.tokens.curr, ["[", "{"])) + if (checkPunctuators(state.tokens.curr, ["[", "{"])) { bracketStack += 1; + } do { - pn = (i === -1) ? state.tokens.next : peek(i); + prev = i === -1 ? state.tokens.curr : pn; + pn = i === -1 ? state.tokens.next : peek(i); pn1 = peek(i + 1); i = i + 1; if (checkPunctuators(pn, ["[", "{"])) { @@ -22110,12 +23517,13 @@ var JSHINT = (function() { } else if (checkPunctuators(pn, ["]", "}"])) { bracketStack -= 1; } - if (pn.identifier && pn.value === "for" && bracketStack === 1) { + if (bracketStack === 1 && pn.identifier && pn.value === "for" && + !checkPunctuator(prev, ".")) { ret.isCompArray = true; ret.notJson = true; break; } - if (checkPunctuators(pn, ["}", "]"]) && bracketStack === 0) { + if (bracketStack === 0 && checkPunctuators(pn, ["}", "]"])) { if (pn1.value === "=") { ret.isDestAssign = true; ret.notJson = true; @@ -22125,7 +23533,7 @@ var JSHINT = (function() { break; } } - if (pn.value === ";") { + if (checkPunctuator(pn, ";")) { ret.isBlock = true; ret.notJson = true; } @@ -22140,10 +23548,10 @@ var JSHINT = (function() { name = tkn.value; } - if (props[name] && _.has(props, name)) { + if (props[name] && name !== "__proto__") { warning("W075", state.tokens.next, msg, name); } else { - props[name] = {}; + props[name] = Object.create(null); } props[name].basic = true; @@ -22175,12 +23583,12 @@ var JSHINT = (function() { state.tokens.curr.accessorType = accessorType; state.nameStack.set(tkn); - if (props[name] && _.has(props, name)) { - if (props[name].basic || props[name][flagName]) { + if (props[name]) { + if ((props[name].basic || props[name][flagName]) && name !== "__proto__") { warning("W075", state.tokens.next, msg, name); } } else { - props[name] = {}; + props[name] = Object.create(null); } props[name][flagName] = tkn; @@ -22188,29 +23596,47 @@ var JSHINT = (function() { function computedPropertyName() { advance("["); - if (!state.option.esnext) { - warning("W119", state.tokens.curr, "computed property names"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "computed property names", "6"); } var value = expression(10); advance("]"); return value; } - // Test whether a given token is a punctuator matching one of the specified values + /** + * Test whether a given token is a punctuator matching one of the specified values + * @param {Token} token + * @param {Array.} values + * @returns {boolean} + */ function checkPunctuators(token, values) { - return token.type === "(punctuator)" && _.contains(values, token.value); + if (token.type === "(punctuator)") { + return _.contains(values, token.value); + } + return false; + } + + /** + * Test whether a given token is a punctuator matching the specified value + * @param {Token} token + * @param {string} value + * @returns {boolean} + */ + function checkPunctuator(token, value) { + return token.type === "(punctuator)" && token.value === value; } // Check whether this function has been reached for a destructuring assign with undeclared values function destructuringAssignOrJsonValue() { - // lookup for the assignment (esnext only) + // lookup for the assignment (ECMAScript 6 only) // if it has semicolons, it is a block, so go parse it as a block // or it's not a block, but there are assignments, check for undeclared variables var block = lookupBlockType(); if (block.notJson) { - if (!state.inESNext() && block.isDestAssign) { - warning("W104", state.tokens.curr, "destructuring assignment"); + if (!state.inES6() && block.isDestAssign) { + warning("W104", state.tokens.curr, "destructuring assignment", "6"); } statements(); // otherwise parse json value @@ -22268,7 +23694,7 @@ var JSHINT = (function() { if (v.unused) warning("W098", v.token, v.raw_text || v.value); if (v.undef) - isundef(v.funct, "W117", v.token, v.value); + state.funct["(scope)"].block.use(v.value, v.token); }); _carrays.splice(-1, 1); _current = _carrays[_carrays.length - 1]; @@ -22285,7 +23711,7 @@ var JSHINT = (function() { if (_current && _current.mode === "use") { if (use(v)) { _current.variables.push({ - funct: funct, + funct: state.funct, token: state.tokens.curr, value: v, undef: true, @@ -22298,7 +23724,7 @@ var JSHINT = (function() { // check if the variable has been used previously if (!declare(v)) { _current.variables.push({ - funct: funct, + funct: state.funct, token: state.tokens.curr, value: v, undef: false, @@ -22308,14 +23734,14 @@ var JSHINT = (function() { return true; // When we are in the "generate" state of the list comp, } else if (_current && _current.mode === "generate") { - isundef(funct, "W117", state.tokens.curr, v); + state.funct["(scope)"].block.use(v, state.tokens.curr); return true; // When we are in "filter" state, } else if (_current && _current.mode === "filter") { // we check whether current variable has been declared if (use(v)) { // if not we warn about it - isundef(funct, "W117", state.tokens.curr, v); + state.funct["(scope)"].block.use(v, state.tokens.curr); } return true; } @@ -22410,141 +23836,6 @@ var JSHINT = (function() { } } - var warnUnused = function(name, tkn, type, unused_opt) { - var line = tkn.line; - var chr = tkn.from; - var raw_name = tkn.raw_text || name; - - if (unused_opt === undefined) { - unused_opt = state.option.unused; - } - - if (unused_opt === true) { - unused_opt = "last-param"; - } - - var warnable_types = { - "vars": ["var"], - "last-param": ["var", "param"], - "strict": ["var", "param", "last-param"] - }; - - if (unused_opt) { - if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { - if (!tkn.exported) { - warningAt("W098", line, chr, raw_name); - } - } - } - - unuseds.push({ - name: name, - line: line, - character: chr - }); - }; - - var blockScope = function() { - var _current = {}; - var _variables = [_current]; - - function _checkBlockLabels() { - for (var t in _current) { - var label = _current[t], - labelType = label["(type)"]; - if (labelType === "unused" || (labelType === "const" && label["(unused)"])) { - if (state.option.unused) { - var tkn = _current[t]["(token)"]; - // Don't report exported labels as unused - if (tkn.exported) { - continue; - } - - warnUnused(t, tkn, "var"); - } - } - } - } - - function _getLabel(l) { - for (var i = _variables.length - 1 ; i >= 0; --i) { - if (_.has(_variables[i], l) && !_variables[i][l]["(shadowed)"]) { - return _variables[i]; - } - } - } - - return { - stack: function() { - _current = {}; - _variables.push(_current); - }, - - unstack: function() { - _checkBlockLabels(); - _variables.splice(_variables.length - 1, 1); - _current = _.last(_variables); - }, - - getlabel: _getLabel, - - labeltype: function(l) { - // returns a labels type or null if not present - var block = _getLabel(l); - if (block) { - return block[l]["(type)"]; - } - return null; - }, - - shadow: function(name) { - for (var i = _variables.length - 1; i >= 0; i--) { - if (_.has(_variables[i], name)) { - _variables[i][name]["(shadowed)"] = true; - } - } - }, - - unshadow: function(name) { - for (var i = _variables.length - 1; i >= 0; i--) { - if (_.has(_variables[i], name)) { - _variables[i][name]["(shadowed)"] = false; - } - } - }, - - atTop: function() { - return _variables.length === 1; - }, - - setExported: function(id) { - if (funct["(blockscope)"].atTop()) { - var item = _current[id]; - if (item && item["(token)"]) { - item["(token)"].exported = true; - } - } - }, - - current: { - labeltype: function(t) { - if (_current[t]) { - return _current[t]["(type)"]; - } - return null; - }, - - has: function(t) { - return _.has(_current, t); - }, - - add: function(t, type, tok) { - _current[t] = { "(type)" : type, "(token)": tok, "(shadowed)": false, "(unused)": true }; - } - } - }; - }; - var escapeRegex = function(str) { return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); }; @@ -22576,7 +23867,7 @@ var JSHINT = (function() { combine(predefined, g || {}); declared = Object.create(null); - exported = Object.create(null); + var exported = Object.create(null); // Variables that live outside the current file function each(obj, cb) { if (!obj) @@ -22617,14 +23908,6 @@ var JSHINT = (function() { } else { var optionKey = optionKeys[x]; newOptionObj[optionKey] = o[optionKey]; - if (optionKey === "es5") { - if (o[optionKey]) { - warning("I003"); - } - } - - if (optionKeys[x] === "newcap" && o[optionKey] === false) - newOptionObj["(explicitNewcap)"] = true; } } } @@ -22636,25 +23919,30 @@ var JSHINT = (function() { state.option.maxerr = state.option.maxerr || 50; indent = 1; - global = Object.create(predefined); - scope = global; - funct = functor("(global)", null, scope, { + var scopeManagerInst = scopeManager(state, predefined, exported, declared); + scopeManagerInst.on("warning", function(ev) { + warning.apply(null, [ ev.code, ev.token].concat(ev.data)); + }); + + scopeManagerInst.on("error", function(ev) { + error.apply(null, [ ev.code, ev.token ].concat(ev.data)); + }); + + state.funct = functor("(global)", null, { "(global)" : true, - "(blockscope)": blockScope(), + "(scope)" : scopeManagerInst, "(comparray)" : arrayComprehension(), "(metrics)" : createMetrics(state.tokens.next) }); - functions = [funct]; + functions = [state.funct]; urls = []; stack = null; member = {}; membersOnly = null; - implied = {}; inblock = false; lookahead = []; - unuseds = []; if (!isString(s) && !Array.isArray(s)) { errorAt("E004", 0); @@ -22729,7 +24017,7 @@ var JSHINT = (function() { }); lex.on("fatal", function(ev) { - quit("E041", ev.line, ev.from); + quit("E041", ev); }); lex.on("Identifier", function(ev) { @@ -22753,15 +24041,15 @@ var JSHINT = (function() { } } - assume(); - - // combine the passed globals after we've assumed all our options - combine(predefined, g || {}); - - //reset values - comma.first = true; - try { + assume(); + + // combine the passed globals after we've assumed all our options + combine(predefined, g || {}); + + //reset values + parseComma.first = true; + advance(); switch (state.tokens.next.id) { case "{": @@ -22771,12 +24059,9 @@ var JSHINT = (function() { default: directives(); - if (state.isStrict()) { - if (!state.option.globalstrict) { - if (!(state.option.module || state.option.node || state.option.phantom || - state.option.browserify)) { - warning("W097", state.tokens.prev); - } + if (state.directive["use strict"]) { + if (!state.allowsGlobalUsd()) { + warning("W097", state.tokens.prev); } } @@ -22784,126 +24069,10 @@ var JSHINT = (function() { } if (state.tokens.next.id !== "(end)") { - quit("E041", state.tokens.curr.line); + quit("E041", state.tokens.curr); } - funct["(blockscope)"].unstack(); - - var markDefined = function(name, context) { - do { - if (typeof context[name] === "string") { - // JSHINT marks unused variables as 'unused' and - // unused function declaration as 'unction'. This - // code changes such instances back 'var' and - // 'closure' so that the code in JSHINT.data() - // doesn't think they're unused. - - if (context[name] === "unused") - context[name] = "var"; - else if (context[name] === "unction") - context[name] = "closure"; - - return true; - } - - context = context["(context)"]; - } while (context); - - return false; - }; - - var clearImplied = function(name, line) { - if (!implied[name]) - return; - - var newImplied = []; - for (var i = 0; i < implied[name].length; i += 1) { - if (implied[name][i] !== line) - newImplied.push(implied[name][i]); - } - - if (newImplied.length === 0) - delete implied[name]; - else - implied[name] = newImplied; - }; - - var checkUnused = function(func, key) { - var type = func[key]; - var tkn = func["(tokens)"][key]; - - if (key.charAt(0) === "(") - return; - - if (type !== "unused" && type !== "unction") - return; - - // Params are checked separately from other variables. - if (func["(params)"] && func["(params)"].indexOf(key) !== -1) - return; - - // Variable is in global scope and defined as exported. - if (func["(global)"] && _.has(exported, key)) - return; - - warnUnused(key, tkn, "var"); - }; - - // Check queued 'x is not defined' instances to see if they're still undefined. - for (i = 0; i < JSHINT.undefs.length; i += 1) { - k = JSHINT.undefs[i].slice(0); - - if (markDefined(k[2].value, k[0]) || k[2].forgiveUndef) { - clearImplied(k[2].value, k[2].line); - } else if (state.option.undef) { - warning.apply(warning, k.slice(1)); - } - } - - functions.forEach(function(func) { - if (func["(unusedOption)"] === false) { - return; - } - - for (var key in func) { - if (_.has(func, key)) { - checkUnused(func, key); - } - } - - if (!func["(params)"]) - return; - - var params = func["(params)"].slice(); - var param = params.pop(); - var type, unused_opt; - - while (param) { - type = func[param]; - unused_opt = func["(unusedOption)"] || state.option.unused; - unused_opt = unused_opt === true ? "last-param" : unused_opt; - - // 'undefined' is a special case for (function(window, undefined) { ... })(); - // patterns. - - if (param === "undefined") - return; - - if (type === "unused" || type === "unction") { - warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); - } else if (unused_opt === "last-param") { - return; - } - - param = params.pop(); - } - }); - - for (var key in declared) { - if (_.has(declared, key) && !_.has(global, key) && !_.has(exported, key)) { - warnUnused(key, declared[key], "var"); - } - } + state.funct["(scope)"].unstack(); } catch (err) { if (err && err.name === "JSHintError") { @@ -22912,7 +24081,7 @@ var JSHINT = (function() { scope : "(main)", raw : err.raw, code : err.code, - reason : err.message, + reason : err.reason, line : err.line || nt.line, character : err.character || nt.from }, null); @@ -22950,8 +24119,6 @@ var JSHINT = (function() { options: state.option }; - var implieds = []; - var members = []; var fu, f, i, j, n, globals; if (itself.errors.length) { @@ -22962,24 +24129,16 @@ var JSHINT = (function() { data.json = true; } - for (n in implied) { - if (_.has(implied, n)) { - implieds.push({ - name: n, - line: implied[n] - }); - } - } - - if (implieds.length > 0) { - data.implieds = implieds; + var impliedGlobals = state.funct["(scope)"].getImpliedGlobals(); + if (impliedGlobals.length > 0) { + data.implieds = impliedGlobals; } if (urls.length > 0) { data.urls = urls; } - globals = Object.keys(scope); + globals = state.funct["(scope)"].getUsedOrDefinedGlobals(); if (globals.length > 0) { data.globals = globals; } @@ -23007,18 +24166,18 @@ var JSHINT = (function() { fu.metrics = { complexity: f["(metrics)"].ComplexityCount, - parameters: (f["(params)"] || []).length, + parameters: f["(metrics)"].arity, statements: f["(metrics)"].statementCount }; data.functions.push(fu); } + var unuseds = state.funct["(scope)"].getUnuseds(); if (unuseds.length > 0) { data.unused = unuseds; } - members = []; for (n in member) { if (typeof member[n] === "number") { data.member = member; @@ -23039,123 +24198,8 @@ if (typeof exports === "object" && exports) { exports.JSHINT = JSHINT; } -},{"./lex.js":13,"./messages.js":14,"./options.js":16,"./reg.js":17,"./state.js":18,"./style.js":19,"./vars.js":20,"console-browserify":10,"events":5,"lodash":12}]},{},[]); +},{"./lex.js":13,"./messages.js":14,"./options.js":16,"./reg.js":17,"./scope-manager.js":18,"./state.js":19,"./style.js":20,"./vars.js":21,"console-browserify":10,"events":5,"lodash":12}]},{},[]); JSHINT = require('jshint').JSHINT; if (typeof exports === 'object' && exports) exports.JSHINT = JSHINT; -}()); -/*jshint boss: true, rhino: true, unused: true, undef: true, quotmark: double */ -/*global JSHINT, readFully */ - -(function(args) { - "use strict"; - - var filenames = []; - var flags = {}; - var opts = {}; - var globals = {}; - var retval = 0; - var readf = (typeof readFully === "function" ? readFully : readFile); - - var optstr; // arg1=val1,arg2=val2,... - var predef; // global1=true,global2,global3,... - - args.forEach(function(arg) { - if (arg.indexOf("--") === 0) { - // Configuration Flags might be boolean or will be split into name and value - if (arg.indexOf("=") > -1) { - var o = arg.split("="); - flags[o[0].slice(2)] = o[1]; - } else { - flags[arg.slice(2)] = true; - } - - return; - } else if (arg.indexOf("=") > -1) { - // usual rhino configuration, like "boss=true,browser=true" - if (!optstr) { - // First time it's the options. - optstr = arg; - } else { - predef = arg; - } - - return; - } - - if (optstr) { - predef = arg; - return; - } - - filenames.push(arg); - }); - - if (filenames.length === 0) { - print("Usage: jshint.js file.js"); - quit(1); - } - - // If a config flag has been provided, try and load that - if ("config" in flags) { - var cfgFileContent; - try { - cfgFileContent = readf(flags.config); - } catch (e) { - print("Could not read config file " + flags.config); - quit(1); - } - - opts = JSON.parse(cfgFileContent); - } - - if (optstr) { - optstr.split(",").forEach(function(arg) { - var o = arg.split("="); - if (o[0] === "indent") { - opts[o[0]] = parseInt(o[1], 10); - } else { - opts[o[0]] = (function(ov) { - switch (ov) { - case "true": - return true; - case "false": - return false; - default: - return ov; - } - }(o[1])); - } - }); - } - - globals = opts.globals || {}; - delete(opts.globals); - - if (predef) { - predef.split(",").forEach(function(arg) { - var global = arg.split("="); - globals[global[0]] = global[1] === "true" ? true : false; - }); - } - - filenames.forEach(function(name) { - var input = readf(name); - - if (!input) { - print("jshint: Couldn't open file " + name); - quit(1); - } - - if (!JSHINT(input, opts, globals)) { - for (var i = 0, err; err = JSHINT.errors[i]; i += 1) { - print(err.reason + " (" + name + ":" + err.line + ":" + err.character + ")"); - print("> " + (err.evidence || "").replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); - print(""); - } - retval = 2; - } - }); - - quit(retval); -}(arguments)); +}()); \ No newline at end of file diff --git a/js/node/node_modules/jshint/dist/jshint.js b/js/node/node_modules/jshint/dist/jshint.js index 744dabab9d..59c10b9945 100644 --- a/js/node/node_modules/jshint/dist/jshint.js +++ b/js/node/node_modules/jshint/dist/jshint.js @@ -1,4 +1,4 @@ -/*! 2.7.0 */ +/*! 2.9.2 */ var JSHINT; if (typeof window === 'undefined') window = {}; (function () { @@ -1486,10 +1486,10 @@ function now() { (function (global){ /** * @license - * lodash 3.6.0 (Custom Build) + * lodash 3.7.0 (Custom Build) * Build: `lodash modern -d -o ./index.js` * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.2 + * Based on Underscore.js 1.8.3 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ @@ -1499,7 +1499,7 @@ function now() { var undefined; /** Used as the semantic version number. */ - var VERSION = '3.6.0'; + var VERSION = '3.7.0'; /** Used to compose bitmasks for wrapper metadata. */ var BIND_FLAG = 1, @@ -1573,30 +1573,10 @@ function now() { reEvaluate = /<%([\s\S]+?)%>/g, reInterpolate = /<%=([\s\S]+?)%>/g; - /** - * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - */ - var reComboMarks = /[\u0300-\u036f\ufe20-\ufe23]/g; - - /** - * Used to match [ES template delimiters](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components). - */ - var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - - /** Used to match `RegExp` flags from their coerced string values. */ - var reFlags = /\w*$/; - - /** Used to detect hexadecimal string values. */ - var reHexPrefix = /^0[xX]/; - - /** Used to detect host constructors (Safari > 5). */ - var reHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ - var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; - - /** Used to ensure capturing order of template delimiters. */ - var reNoMatch = /($^)/; + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]+|(["'])(?:(?!\1)[^\n\\]|\\.)*?)\1\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; /** * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). @@ -1606,6 +1586,30 @@ function now() { var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, reHasRegExpChars = RegExp(reRegExpChars.source); + /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ + var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to match [ES template delimiters](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components). */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect hexadecimal string values. */ + var reHasHexPrefix = /^0[xX]/; + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ + var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + /** Used to match unescaped characters in compiled string literals. */ var reUnescapedString = /['\n\r\u2028\u2029\\]/g; @@ -1743,7 +1747,7 @@ function now() { var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; /** Detect free variable `global` from Node.js. */ - var freeGlobal = freeExports && freeModule && typeof global == 'object' && global; + var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; /** Detect free variable `self`. */ var freeSelf = objectTypes[typeof self] && self && self.Object && self; @@ -1778,10 +1782,10 @@ function now() { var valIsReflexive = value === value, othIsReflexive = other === other; - if (value > other || !valIsReflexive || (typeof value == 'undefined' && othIsReflexive)) { + if (value > other || !valIsReflexive || (value === undefined && othIsReflexive)) { return 1; } - if (value < other || !othIsReflexive || (typeof other == 'undefined' && valIsReflexive)) { + if (value < other || !othIsReflexive || (other === undefined && valIsReflexive)) { return -1; } } @@ -1924,7 +1928,7 @@ function now() { * Used by `_.sortByOrder` to compare multiple properties of each element * in a collection and stable sort them in the following order: * - * If orders is unspecified, sort in ascending order for all properties. + * If `orders` is unspecified, sort in ascending order for all properties. * Otherwise, for each property, sort in ascending order if its corresponding value in * orders is true, and descending order if false. * @@ -2201,9 +2205,6 @@ function now() { /** Used to resolve the decompiled source of functions. */ var fnToString = Function.prototype.toString; - /** Used to the length of n-tuples for `_.unzip`. */ - var getLength = baseProperty('length'); - /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; @@ -2220,7 +2221,7 @@ function now() { var oldDash = context._; /** Used to detect if a method is native. */ - var reNative = RegExp('^' + + var reIsNative = RegExp('^' + escapeRegExp(objToString) .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); @@ -2231,8 +2232,10 @@ function now() { ceil = Math.ceil, clearTimeout = context.clearTimeout, floor = Math.floor, + getOwnPropertySymbols = isNative(getOwnPropertySymbols = Object.getOwnPropertySymbols) && getOwnPropertySymbols, getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, push = arrayProto.push, + preventExtensions = isNative(Object.preventExtensions = Object.preventExtensions) && preventExtensions, propertyIsEnumerable = objectProto.propertyIsEnumerable, Set = isNative(Set = context.Set) && Set, setTimeout = context.setTimeout, @@ -2252,6 +2255,22 @@ function now() { return result; }()); + /** Used as `baseAssign`. */ + var nativeAssign = (function() { + // Avoid `Object.assign` in Firefox 34-37 which have an early implementation + // with a now defunct try/catch behavior. See https://bugzilla.mozilla.org/show_bug.cgi?id=1103344 + // for more details. + // + // Use `Object.preventExtensions` on a plain object instead of simply using + // `Object('x')` because Chrome and IE fail to throw an error when attempting + // to assign values to readonly indexes of strings in strict mode. + var object = { '1': 0 }, + func = preventExtensions && isNative(func = Object.assign) && func; + + try { func(preventExtensions(object), 'xo'); } catch(e) {} + return !object[1] && func; + }()); + /* Native method references for those with the same name as other `lodash` methods. */ var nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, @@ -2329,8 +2348,8 @@ function now() { * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`, * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`, * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`, - * `keysIn`, `map`, `mapValues`, `matches`, `matchesProperty`, `memoize`, `merge`, - * `mixin`, `negate`, `noop`, `omit`, `once`, `pairs`, `partial`, `partialRight`, + * `keysIn`, `map`, `mapValues`, `matches`, `matchesProperty`, `memoize`, + * `merge`, `mixin`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `reverse`, * `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, @@ -2344,15 +2363,15 @@ function now() { * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `has`, * `identity`, `includes`, `indexOf`, `inRange`, `isArguments`, `isArray`, - * `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, - * `isFinite`,`isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`, - * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, - * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`, - * `noConflict`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, - * `random`, `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`, - * `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, - * `startCase`, `startsWith`, `sum`, `template`, `trim`, `trimLeft`, - * `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words` + * `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, `isFinite` + * `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, + * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, + * `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`, `noConflict`, + * `noop`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, + * `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, `startsWith`, + * `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, `unescape`, + * `uniqueId`, `value`, and `words` * * The wrapper method `sample` will return a wrapped value when `n` is provided, * otherwise an unwrapped value is returned. @@ -2367,8 +2386,8 @@ function now() { * var wrapped = _([1, 2, 3]); * * // returns an unwrapped value - * wrapped.reduce(function(sum, n) { - * return sum + n; + * wrapped.reduce(function(total, n) { + * return total + n; * }); * // => 6 * @@ -2428,6 +2447,12 @@ function now() { var support = lodash.support = {}; (function(x) { + var Ctor = function() { this.x = x; }, + object = { '0': x, 'length': x }, + props = []; + + Ctor.prototype = { 'valueOf': x, 'y': x }; + for (var key in new Ctor) { props.push(key); } /** * Detect if functions can be decompiled by `Function#toString` @@ -2465,8 +2490,8 @@ function now() { * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat * `arguments` object indexes as non-enumerable and fail `hasOwnProperty` - * checks for indexes that exceed their function's formal parameters with - * associated values of `0`. + * checks for indexes that exceed the number of function parameters and + * whose associated argument values are `0`. * * @memberOf _.support * @type boolean @@ -2476,7 +2501,7 @@ function now() { } catch(e) { support.nonEnumArgs = true; } - }(0, 0)); + }(1, 0)); /** * By default, the template delimiters used by lodash are like those in @@ -2723,7 +2748,7 @@ function now() { } /** - * Adds `value` to `key` of the cache. + * Sets `value` to `key` of the cache. * * @private * @name set @@ -3056,13 +3081,13 @@ function now() { * @returns {*} Returns the value to assign to the destination object. */ function assignDefaults(objectValue, sourceValue) { - return typeof objectValue == 'undefined' ? sourceValue : objectValue; + return objectValue === undefined ? sourceValue : objectValue; } /** * Used by `_.template` to customize its `_.assign` use. * - * **Note:** This method is like `assignDefaults` except that it ignores + * **Note:** This function is like `assignDefaults` except that it ignores * inherited property values when checking if a property is `undefined`. * * @private @@ -3073,26 +3098,26 @@ function now() { * @returns {*} Returns the value to assign to the destination object. */ function assignOwnDefaults(objectValue, sourceValue, key, object) { - return (typeof objectValue == 'undefined' || !hasOwnProperty.call(object, key)) + return (objectValue === undefined || !hasOwnProperty.call(object, key)) ? sourceValue : objectValue; } /** - * The base implementation of `_.assign` without support for argument juggling, - * multiple sources, and `this` binding `customizer` functions. + * A specialized version of `_.assign` for customizing assigned values without + * support for argument juggling, multiple sources, and `this` binding `customizer` + * functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. - * @param {Function} [customizer] The function to customize assigning values. - * @returns {Object} Returns the destination object. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. */ - function baseAssign(object, source, customizer) { + function assignWith(object, source, customizer) { var props = keys(source); - if (!customizer) { - return baseCopy(source, object, props); - } + push.apply(props, getSymbols(source)); + var index = -1, length = props.length; @@ -3102,7 +3127,7 @@ function now() { result = customizer(value, source[key], key, object, source); if ((result === result ? (result !== value) : (value === value)) || - (typeof value == 'undefined' && !(key in object))) { + (value === undefined && !(key in object))) { object[key] = result; } } @@ -3110,12 +3135,27 @@ function now() { } /** - * The base implementation of `_.at` without support for strings and individual - * key arguments. + * The base implementation of `_.assign` without support for argument juggling, + * multiple sources, and `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + var baseAssign = nativeAssign || function(object, source) { + return source == null + ? object + : baseCopy(source, getSymbols(source), baseCopy(source, keys(source), object)); + }; + + /** + * The base implementation of `_.at` without support for string collections + * and individual key arguments. * * @private * @param {Array|Object} collection The collection to iterate over. - * @param {number[]|string[]} [props] The property names or indexes of elements to pick. + * @param {number[]|string[]} props The property names or indexes of elements to pick. * @returns {Array} Returns the new array of picked elements. */ function baseAt(collection, props) { @@ -3128,7 +3168,6 @@ function now() { while(++index < propsLength) { var key = props[index]; if (isArr) { - key = parseFloat(key); result[index] = isIndex(key, length) ? collection[key] : undefined; } else { result[index] = collection[key]; @@ -3138,19 +3177,17 @@ function now() { } /** - * Copies the properties of `source` to `object`. + * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. - * @param {Object} [object={}] The object to copy properties to. * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. * @returns {Object} Returns `object`. */ - function baseCopy(source, object, props) { - if (!props) { - props = object; - object = {}; - } + function baseCopy(source, props, object) { + object || (object = {}); + var index = -1, length = props.length; @@ -3174,7 +3211,7 @@ function now() { function baseCallback(func, thisArg, argCount) { var type = typeof func; if (type == 'function') { - return typeof thisArg == 'undefined' + return thisArg === undefined ? func : bindCallback(func, thisArg, argCount); } @@ -3184,9 +3221,9 @@ function now() { if (type == 'object') { return baseMatches(func); } - return typeof thisArg == 'undefined' - ? baseProperty(func + '') - : baseMatchesProperty(func + '', thisArg); + return thisArg === undefined + ? property(func) + : baseMatchesProperty(func, thisArg); } /** @@ -3208,7 +3245,7 @@ function now() { if (customizer) { result = object ? customizer(value, key, object) : customizer(value); } - if (typeof result != 'undefined') { + if (result !== undefined) { return result; } if (!isObject(value)) { @@ -3227,7 +3264,7 @@ function now() { if (tag == objectTag || tag == argsTag || (isFunc && !object)) { result = initCloneObject(isFunc ? {} : value); if (!isDeep) { - return baseCopy(value, result, keys(value)); + return baseAssign(result, value); } } else { return cloneableTags[tag] @@ -3398,7 +3435,7 @@ function now() { if (start < 0) { start = -start > length ? 0 : (length + start); } - end = (typeof end == 'undefined' || end > length) ? length : (+end || 0); + end = (end === undefined || end > length) ? length : (+end || 0); if (end < 0) { end += length; } @@ -3495,7 +3532,7 @@ function now() { /** * The base implementation of `baseForIn` and `baseForOwn` which iterates * over `object` properties returned by `keysFunc` invoking `iteratee` for - * each property. Iterator functions may exit iteration early by explicitly + * each property. Iteratee functions may exit iteration early by explicitly * returning `false`. * * @private @@ -3581,6 +3618,32 @@ function now() { return result; } + /** + * The base implementation of `get` without support for string paths + * and default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path of the property to get. + * @param {string} [pathKey] The key representation of path. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path, pathKey) { + if (object == null) { + return; + } + if (pathKey !== undefined && pathKey in toObject(object)) { + path = [pathKey]; + } + var index = -1, + length = path.length; + + while (object != null && ++index < length) { + var result = object = object[path[index]]; + } + return result; + } + /** * The base implementation of `_.isEqual` without support for `this` binding * `customizer` functions. @@ -3649,27 +3712,23 @@ function now() { othIsArr = isTypedArray(other); } } - var objIsObj = (objTag == objectTag || (isLoose && objTag == funcTag)), - othIsObj = (othTag == objectTag || (isLoose && othTag == funcTag)), + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, isSameTag = objTag == othTag; if (isSameTag && !(objIsArr || objIsObj)) { return equalByTag(object, other, objTag); } - if (isLoose) { - if (!isSameTag && !(objIsObj && othIsObj)) { - return false; - } - } else { + if (!isLoose) { var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); if (valWrapped || othWrapped) { return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); } - if (!isSameTag) { - return false; - } + } + if (!isSameTag) { + return false; } // Assume cyclic values are equal. // For more information on detecting circular references see https://es5.github.io/#JO. @@ -3726,10 +3785,10 @@ function now() { srcValue = values[index]; if (noCustomizer && strictCompareFlags[index]) { - var result = typeof objValue != 'undefined' || (key in object); + var result = objValue !== undefined || (key in object); } else { result = customizer ? customizer(objValue, srcValue, key) : undefined; - if (typeof result == 'undefined') { + if (result === undefined) { result = baseIsEqual(srcValue, objValue, customizer, true); } } @@ -3750,9 +3809,12 @@ function now() { * @returns {Array} Returns the new mapped array. */ function baseMap(collection, iteratee) { - var result = []; + var index = -1, + length = getLength(collection), + result = isLength(length) ? Array(length) : []; + baseEach(collection, function(value, key, collection) { - result.push(iteratee(value, key, collection)); + result[++index] = iteratee(value, key, collection); }); return result; } @@ -3777,8 +3839,10 @@ function now() { if (isStrictComparable(value)) { return function(object) { - return object != null && object[key] === value && - (typeof value != 'undefined' || (key in toObject(object))); + if (object == null) { + return false; + } + return object[key] === value && (value !== undefined || (key in toObject(object))); }; } } @@ -3796,23 +3860,37 @@ function now() { } /** - * The base implementation of `_.matchesProperty` which does not coerce `key` - * to a string. + * The base implementation of `_.matchesProperty` which does not which does + * not clone `value`. * * @private - * @param {string} key The key of the property to get. + * @param {string} path The path of the property to get. * @param {*} value The value to compare. * @returns {Function} Returns the new function. */ - function baseMatchesProperty(key, value) { - if (isStrictComparable(value)) { - return function(object) { - return object != null && object[key] === value && - (typeof value != 'undefined' || (key in toObject(object))); - }; - } + function baseMatchesProperty(path, value) { + var isArr = isArray(path), + isCommon = isKey(path) && isStrictComparable(value), + pathKey = (path + ''); + + path = toPath(path); return function(object) { - return object != null && baseIsEqual(value, object[key], null, true); + if (object == null) { + return false; + } + var key = pathKey; + object = toObject(object); + if ((isArr || !isCommon) && !(key in object)) { + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + if (object == null) { + return false; + } + key = last(path); + object = toObject(object); + } + return object[key] === value + ? (value !== undefined || (key in object)) + : baseIsEqual(value, object[key], null, true); }; } @@ -3826,29 +3904,39 @@ function now() { * @param {Function} [customizer] The function to customize merging properties. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates values with source counterparts. - * @returns {Object} Returns the destination object. + * @returns {Object} Returns `object`. */ function baseMerge(object, source, customizer, stackA, stackB) { if (!isObject(object)) { return object; } var isSrcArr = isLength(source.length) && (isArray(source) || isTypedArray(source)); - (isSrcArr ? arrayEach : baseForOwn)(source, function(srcValue, key, source) { + if (!isSrcArr) { + var props = keys(source); + push.apply(props, getSymbols(source)); + } + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } if (isObjectLike(srcValue)) { stackA || (stackA = []); stackB || (stackB = []); - return baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); + baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); } - var value = object[key], - result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = typeof result == 'undefined'; + else { + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; - if (isCommon) { - result = srcValue; - } - if ((isSrcArr || typeof result != 'undefined') && - (isCommon || (result === result ? (result !== value) : (value === value)))) { - object[key] = result; + if (isCommon) { + result = srcValue; + } + if ((isSrcArr || result !== undefined) && + (isCommon || (result === result ? (result !== value) : (value === value)))) { + object[key] = result; + } } }); return object; @@ -3881,14 +3969,14 @@ function now() { } var value = object[key], result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = typeof result == 'undefined'; + isCommon = result === undefined; if (isCommon) { result = srcValue; if (isLength(srcValue.length) && (isArray(srcValue) || isTypedArray(srcValue))) { result = isArray(value) ? value - : ((value && value.length) ? arrayCopy(value) : []); + : (getLength(value) ? arrayCopy(value) : []); } else if (isPlainObject(srcValue) || isArguments(srcValue)) { result = isArguments(value) @@ -3913,7 +4001,7 @@ function now() { } /** - * The base implementation of `_.property` which does not coerce `key` to a string. + * The base implementation of `_.property` without support for deep paths. * * @private * @param {string} key The key of the property to get. @@ -3925,6 +4013,42 @@ function now() { }; } + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new function. + */ + function basePropertyDeep(path) { + var pathKey = (path + ''); + path = toPath(path); + return function(object) { + return baseGet(object, path, pathKey); + }; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * index arguments and capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = indexes.length; + while (length--) { + var index = parseFloat(indexes[length]); + if (index != previous && isIndex(index)) { + var previous = index; + splice.call(array, index, 1); + } + } + return array; + } + /** * The base implementation of `_.random` without support for argument juggling * and returning floating-point numbers. @@ -3991,7 +4115,7 @@ function now() { if (start < 0) { start = -start > length ? 0 : (length + start); } - end = (typeof end == 'undefined' || end > length) ? length : (+end || 0); + end = (end === undefined || end > length) ? length : (+end || 0); if (end < 0) { end += length; } @@ -4050,23 +4174,19 @@ function now() { * * @private * @param {Array|Object|string} collection The collection to iterate over. - * @param {string[]} props The property names to sort by. - * @param {boolean[]} orders The sort orders of `props`. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} orders The sort orders of `iteratees`. * @returns {Array} Returns the new sorted array. */ - function baseSortByOrder(collection, props, orders) { - var index = -1, - length = collection.length, - result = isLength(length) ? Array(length) : []; + function baseSortByOrder(collection, iteratees, orders) { + var callback = getCallback(), + index = -1; - baseEach(collection, function(value) { - var length = props.length, - criteria = Array(length); + iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); }); - while (length--) { - criteria[length] = value == null ? undefined : value[props[length]]; - } - result[++index] = { 'criteria': criteria, 'index': index, 'value': value }; + var result = baseMap(collection, function(value) { + var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; }); return baseSortBy(result, function(object, other) { @@ -4146,7 +4266,7 @@ function now() { /** * The base implementation of `_.values` and `_.valuesIn` which creates an * array of `object` property values corresponding to the property names - * returned by `keysFunc`. + * of `props`. * * @private * @param {Object} object The object to query. @@ -4263,7 +4383,7 @@ function now() { var low = 0, high = array ? array.length : 0, valIsNaN = value !== value, - valIsUndef = typeof value == 'undefined'; + valIsUndef = value === undefined; while (low < high) { var mid = floor((low + high) / 2), @@ -4273,7 +4393,7 @@ function now() { if (valIsNaN) { var setLow = isReflexive || retHighest; } else if (valIsUndef) { - setLow = isReflexive && (retHighest || typeof computed != 'undefined'); + setLow = isReflexive && (retHighest || computed !== undefined); } else { setLow = retHighest ? (computed <= value) : (computed < value); } @@ -4300,7 +4420,7 @@ function now() { if (typeof func != 'function') { return identity; } - if (typeof thisArg == 'undefined') { + if (thisArg === undefined) { return func; } switch (argCount) { @@ -4460,38 +4580,32 @@ function now() { * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { - return function() { - var args = arguments, - length = args.length, - object = args[0]; + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 && sources[length - 2], + guard = length > 2 && sources[2], + thisArg = length > 1 && sources[length - 1]; - if (length < 2 || object == null) { - return object; - } - var customizer = args[length - 2], - thisArg = args[length - 1], - guard = args[3]; - - if (length > 3 && typeof customizer == 'function') { + if (typeof customizer == 'function') { customizer = bindCallback(customizer, thisArg, 5); length -= 2; } else { - customizer = (length > 2 && typeof thisArg == 'function') ? thisArg : null; + customizer = typeof thisArg == 'function' ? thisArg : null; length -= (customizer ? 1 : 0); } - if (guard && isIterateeCall(args[1], args[2], guard)) { - customizer = length == 3 ? null : customizer; - length = 2; + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? null : customizer; + length = 1; } - var index = 0; while (++index < length) { - var source = args[index]; + var source = sources[index]; if (source) { assigner(object, source, customizer); } } return object; - }; + }); } /** @@ -4504,7 +4618,7 @@ function now() { */ function createBaseEach(eachFunc, fromRight) { return function(collection, iteratee) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; if (!isLength(length)) { return eachFunc(collection, iteratee); } @@ -4781,7 +4895,7 @@ function now() { */ function createForEach(arrayFunc, eachFunc) { return function(collection, iteratee, thisArg) { - return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection)) + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) ? arrayFunc(collection, iteratee) : eachFunc(collection, bindCallback(iteratee, thisArg, 3)); }; @@ -4796,7 +4910,7 @@ function now() { */ function createForIn(objectFunc) { return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || typeof thisArg != 'undefined') { + if (typeof iteratee != 'function' || thisArg !== undefined) { iteratee = bindCallback(iteratee, thisArg, 3); } return objectFunc(object, iteratee, keysIn); @@ -4812,7 +4926,7 @@ function now() { */ function createForOwn(objectFunc) { return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || typeof thisArg != 'undefined') { + if (typeof iteratee != 'function' || thisArg !== undefined) { iteratee = bindCallback(iteratee, thisArg, 3); } return objectFunc(object, iteratee); @@ -4859,7 +4973,7 @@ function now() { function createReduce(arrayFunc, eachFunc) { return function(collection, iteratee, accumulator, thisArg) { var initFromArray = arguments.length < 3; - return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection)) + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) ? arrayFunc(collection, iteratee, accumulator, initFromArray) : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); }; @@ -5128,7 +5242,7 @@ function now() { ? customizer(othValue, arrValue, index) : customizer(arrValue, othValue, index); } - if (typeof result == 'undefined') { + if (result === undefined) { // Recursively compare arrays (susceptible to call stack limits). if (isLoose) { var othIndex = othLength; @@ -5227,7 +5341,7 @@ function now() { ? customizer(othValue, objValue, key) : customizer(objValue, othValue, key); } - if (typeof result == 'undefined') { + if (result === undefined) { // Recursively compare objects (susceptible to call stack limits). result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB); } @@ -5352,6 +5466,29 @@ function now() { return collection ? result(collection, target, fromIndex) : result; } + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * in Safari on iOS 8.1 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + /** + * Creates an array of the own symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !getOwnPropertySymbols ? constant([]) : function(object) { + return getOwnPropertySymbols(toObject(object)); + }; + /** * Gets the view, applying any `transforms` to the `start` and `end` positions. * @@ -5420,7 +5557,6 @@ function now() { * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * - * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. @@ -5454,6 +5590,25 @@ function now() { return result; } + /** + * Invokes the method at `path` on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function invokePath(object, path, args) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + } + var func = object == null ? object : object[path]; + return func == null ? undefined : func.apply(object, args); + } + /** * Checks if `value` is a valid array-like index. * @@ -5483,7 +5638,7 @@ function now() { } var type = typeof index; if (type == 'number') { - var length = object.length, + var length = getLength(object), prereq = isLength(length) && isIndex(index, length); } else { prereq = type == 'string' && index in object; @@ -5495,6 +5650,26 @@ function now() { return false; } + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + var type = typeof value; + if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { + return true; + } + if (isArray(value)) { + return false; + } + var result = !reIsDeepProp.test(value); + return result || (object != null && value in toObject(object)); + } + /** * Checks if `func` has a lazy counterpart. * @@ -5604,7 +5779,7 @@ function now() { /** * A specialized version of `_.pick` that picks `object` properties specified - * by the `props` array. + * by `props`. * * @private * @param {Object} object The source object. @@ -5730,7 +5905,7 @@ function now() { baseForIn(value, function(subValue, key) { result = key; }); - return typeof result == 'undefined' || hasOwnProperty.call(value, result); + return result === undefined || hasOwnProperty.call(value, result); } /** @@ -5738,7 +5913,7 @@ function now() { * own enumerable property names of `object`. * * @private - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function shimKeys(object) { @@ -5773,7 +5948,7 @@ function now() { if (value == null) { return []; } - if (!isLength(value.length)) { + if (!isLength(getLength(value))) { return values(value); } return isObject(value) ? value : Object(value); @@ -5790,6 +5965,24 @@ function now() { return isObject(value) ? value : Object(value); } + /** + * Converts `value` to property path array if it is not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array} Returns the property path array. + */ + function toPath(value) { + if (isArray(value)) { + return value; + } + var result = []; + baseToString(value).replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + } + /** * Creates a clone of `wrapper`. * @@ -6374,7 +6567,8 @@ function now() { argsLength = arguments.length, caches = [], indexOf = getIndexOf(), - isCommon = indexOf == baseIndexOf; + isCommon = indexOf == baseIndexOf, + result = []; while (++argsIndex < argsLength) { var value = arguments[argsIndex]; @@ -6384,10 +6578,12 @@ function now() { } } argsLength = args.length; + if (argsLength < 2) { + return result; + } var array = args[0], index = -1, length = array ? array.length : 0, - result = [], seen = caches[0]; outer: @@ -6555,17 +6751,8 @@ function now() { array || (array = []); indexes = baseFlatten(indexes); - var length = indexes.length, - result = baseAt(array, indexes); - - indexes.sort(baseCompareAscending); - while (length--) { - var index = parseFloat(indexes[length]); - if (index != previous && isIndex(index)) { - var previous = index; - splice.call(array, index, 1); - } - } + var result = baseAt(array, indexes); + basePullAt(array, indexes.sort(baseCompareAscending)); return result; }); @@ -6609,19 +6796,23 @@ function now() { * // => [2, 4] */ function remove(array, predicate, thisArg) { + var result = []; + if (!(array && array.length)) { + return result; + } var index = -1, - length = array ? array.length : 0, - result = []; + indexes = [], + length = array.length; predicate = getCallback(predicate, thisArg, 3); while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result.push(value); - splice.call(array, index--, 1); - length--; + indexes.push(index); } } + basePullAt(array, indexes); return result; } @@ -6646,7 +6837,7 @@ function now() { /** * Creates a slice of `array` from `start` up to, but not including, `end`. * - * **Note:** This function is used instead of `Array#slice` to support node + * **Note:** This method is used instead of `Array#slice` to support node * lists in IE < 9 and to ensure dense arrays are returned. * * @static @@ -6945,12 +7136,13 @@ function now() { }); /** - * Creates a duplicate-value-free version of an array using `SameValueZero` - * for equality comparisons. Providing `true` for `isSorted` performs a faster - * search algorithm for sorted arrays. If an iteratee function is provided it - * is invoked for each value in the array to generate the criterion by which - * uniqueness is computed. The `iteratee` is bound to `thisArg` and invoked - * with three arguments: (value, index, array). + * Creates a duplicate-free version of an array, using `SameValueZero` for + * equality comparisons, in which only the first occurence of each element + * is kept. Providing `true` for `isSorted` performs a faster search algorithm + * for sorted arrays. If an iteratee function is provided it is invoked for + * each element in the array to generate the criterion by which uniqueness + * is computed. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, array). * * If a property name is provided for `iteratee` the created `_.property` * style callback returns the property value of the given element. @@ -6978,8 +7170,8 @@ function now() { * @returns {Array} Returns the new duplicate-value-free array. * @example * - * _.uniq([1, 2, 1]); - * // => [1, 2] + * _.uniq([2, 1, 2]); + * // => [2, 1] * * // using `isSorted` * _.uniq([1, 1, 2], true); @@ -7429,7 +7621,7 @@ function now() { * // => ['barney', 'pebbles'] */ var at = restParam(function(collection, props) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; if (isLength(length)) { collection = toIterable(collection); } @@ -7534,7 +7726,7 @@ function now() { if (thisArg && isIterateeCall(collection, predicate, thisArg)) { predicate = null; } - if (typeof predicate != 'function' || typeof thisArg != 'undefined') { + if (typeof predicate != 'function' || thisArg !== undefined) { predicate = getCallback(predicate, thisArg, 3); } return func(collection, predicate); @@ -7704,10 +7896,10 @@ function now() { /** * Iterates over elements of `collection` invoking `iteratee` for each element. * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). Iterator functions may exit iteration early + * (value, index|key, collection). Iteratee functions may exit iteration early * by explicitly returning `false`. * - * **Note:** As with other "Collections" methods, objects with a `length` property + * **Note:** As with other "Collections" methods, objects with a "length" property * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` * may be used for object iteration. * @@ -7837,7 +8029,7 @@ function now() { * // => true */ function includes(collection, target, fromIndex, guard) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; if (!isLength(length)) { collection = values(collection); length = collection.length; @@ -7906,16 +8098,16 @@ function now() { }); /** - * Invokes the method named by `methodName` on each element in `collection`, - * returning an array of the results of each invoked method. Any additional - * arguments are provided to each invoked method. If `methodName` is a function - * it is invoked for, and `this` bound to, each element in `collection`. + * Invokes the method at `path` on each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `methodName` is a function it is + * invoked for, and `this` bound to, each element in `collection`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|string} methodName The name of the method to invoke or + * @param {Array|Function|string} path The path of the method to invoke or * the function invoked per iteration. * @param {...*} [args] The arguments to invoke the method with. * @returns {Array} Returns the array of results. @@ -7927,15 +8119,16 @@ function now() { * _.invoke([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ - var invoke = restParam(function(collection, methodName, args) { + var invoke = restParam(function(collection, path, args) { var index = -1, - isFunc = typeof methodName == 'function', - length = collection ? collection.length : 0, + isFunc = typeof path == 'function', + isProp = isKey(path), + length = getLength(collection), result = isLength(length) ? Array(length) : []; baseEach(collection, function(value) { - var func = isFunc ? methodName : (value != null && value[methodName]); - result[++index] = func ? func.apply(value, args) : undefined; + var func = isFunc ? path : (isProp && value != null && value[path]); + result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); }); return result; }); @@ -7972,7 +8165,6 @@ function now() { * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. - * create a `_.property` or `_.matches` style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new mapped array. * @example @@ -8066,13 +8258,13 @@ function now() { }, function() { return [[], []]; }); /** - * Gets the value of `key` from all elements in `collection`. + * Gets the property value of `path` from all elements in `collection`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {string} key The key of the property to pluck. + * @param {Array|string} path The path of the property to pluck. * @returns {Array} Returns the property values. * @example * @@ -8088,8 +8280,8 @@ function now() { * _.pluck(userIndex, 'age'); * // => [36, 40] (iteration order is not guaranteed) */ - function pluck(collection, key) { - return map(collection, baseProperty(key)); + function pluck(collection, path) { + return map(collection, property(path)); } /** @@ -8117,8 +8309,8 @@ function now() { * @returns {*} Returns the accumulated value. * @example * - * _.reduce([1, 2], function(sum, n) { - * return sum + n; + * _.reduce([1, 2], function(total, n) { + * return total + n; * }); * // => 3 * @@ -8290,7 +8482,7 @@ function now() { * // => 7 */ function size(collection) { - var length = collection ? collection.length : 0; + var length = collection ? getLength(collection) : 0; return isLength(length) ? length : keys(collection).length; } @@ -8348,7 +8540,7 @@ function now() { if (thisArg && isIterateeCall(collection, predicate, thisArg)) { predicate = null; } - if (typeof predicate != 'function' || typeof thisArg != 'undefined') { + if (typeof predicate != 'function' || thisArg !== undefined) { predicate = getCallback(predicate, thisArg, 3); } return func(collection, predicate); @@ -8376,9 +8568,8 @@ function now() { * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|Function|Object|string} [iteratee=_.identity] The function - * invoked per iteration. If a property name or an object is provided it is - * used to create a `_.property` or `_.matches` style callback respectively. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new sorted array. * @example @@ -8407,104 +8598,112 @@ function now() { if (collection == null) { return []; } - var index = -1, - length = collection.length, - result = isLength(length) ? Array(length) : []; - if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { iteratee = null; } + var index = -1; iteratee = getCallback(iteratee, thisArg, 3); - baseEach(collection, function(value, key, collection) { - result[++index] = { 'criteria': iteratee(value, key, collection), 'index': index, 'value': value }; + + var result = baseMap(collection, function(value, key, collection) { + return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; }); return baseSortBy(result, compareAscending); } /** - * This method is like `_.sortBy` except that it sorts by property names - * instead of an iteratee function. + * This method is like `_.sortBy` except that it can sort by multiple iteratees + * or property names. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(string|string[])} props The property names to sort by, - * specified as individual property names or arrays of property names. + * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees + * The iteratees to sort by, specified as individual values or arrays of values. * @returns {Array} Returns the new sorted array. * @example * * var users = [ + * { 'user': 'fred', 'age': 48 }, * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 26 }, - * { 'user': 'fred', 'age': 30 } + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 34 } * ]; * * _.map(_.sortByAll(users, ['user', 'age']), _.values); - * // => [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]] + * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] + * + * _.map(_.sortByAll(users, 'user', function(chr) { + * return Math.floor(chr.age / 10); + * }), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] */ - function sortByAll() { - var args = arguments, - collection = args[0], - guard = args[3], - index = 0, - length = args.length - 1; - + var sortByAll = restParam(function(collection, iteratees) { if (collection == null) { return []; } - var props = Array(length); - while (index < length) { - props[index] = args[++index]; + var guard = iteratees[2]; + if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { + iteratees.length = 1; } - if (guard && isIterateeCall(args[1], args[2], guard)) { - props = args[1]; - } - return baseSortByOrder(collection, baseFlatten(props), []); - } + return baseSortByOrder(collection, baseFlatten(iteratees), []); + }); /** * This method is like `_.sortByAll` except that it allows specifying the - * sort orders of the property names to sort by. A truthy value in `orders` - * will sort the corresponding property name in ascending order while a - * falsey value will sort it in descending order. + * sort orders of the iteratees to sort by. A truthy value in `orders` will + * sort the corresponding property name in ascending order while a falsey + * value will sort it in descending order. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. - * @param {string[]} props The property names to sort by. - * @param {boolean[]} orders The sort orders of `props`. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} orders The sort orders of `iteratees`. * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. * @returns {Array} Returns the new sorted array. * @example * * var users = [ - * { 'user': 'barney', 'age': 26 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 30 } + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 36 } * ]; * * // sort by `user` in ascending order and by `age` in descending order * _.map(_.sortByOrder(users, ['user', 'age'], [true, false]), _.values); - * // => [['barney', 36], ['barney', 26], ['fred', 40], ['fred', 30]] + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] */ - function sortByOrder(collection, props, orders, guard) { + function sortByOrder(collection, iteratees, orders, guard) { if (collection == null) { return []; } - if (guard && isIterateeCall(props, orders, guard)) { + if (guard && isIterateeCall(iteratees, orders, guard)) { orders = null; } - if (!isArray(props)) { - props = props == null ? [] : [props]; + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; } if (!isArray(orders)) { orders = orders == null ? [] : [orders]; } - return baseSortByOrder(collection, props, orders); + return baseSortByOrder(collection, iteratees, orders); } /** @@ -8657,7 +8856,8 @@ function now() { return function() { if (--n > 0) { result = func.apply(this, arguments); - } else { + } + if (n <= 1) { func = null; } return result; @@ -8672,7 +8872,7 @@ function now() { * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for partially applied arguments. * - * **Note:** Unlike native `Function#bind` this method does not set the `length` + * **Note:** Unlike native `Function#bind` this method does not set the "length" * property of bound functions. * * @static @@ -8714,7 +8914,7 @@ function now() { * of method names. If no method names are provided all enumerable function * properties, own and inherited, of `object` are bound. * - * **Note:** This method does not set the `length` property of bound functions. + * **Note:** This method does not set the "length" property of bound functions. * * @static * @memberOf _ @@ -8755,7 +8955,7 @@ function now() { * * This method differs from `_.bind` by allowing bound functions to reference * methods that may be redefined or don't yet exist. - * See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern) + * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) * for more details. * * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic @@ -8812,7 +9012,7 @@ function now() { * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for provided arguments. * - * **Note:** This method does not set the `length` property of curried functions. + * **Note:** This method does not set the "length" property of curried functions. * * @static * @memberOf _ @@ -8851,7 +9051,7 @@ function now() { * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for provided arguments. * - * **Note:** This method does not set the `length` property of curried functions. + * **Note:** This method does not set the "length" property of curried functions. * * @static * @memberOf _ @@ -9263,7 +9463,7 @@ function now() { * // `initialize` invokes `createApplication` once */ function once(func) { - return before(func, 2); + return before(2, func); } /** @@ -9274,7 +9474,7 @@ function now() { * The `_.partial.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * - * **Note:** This method does not set the `length` property of partially + * **Note:** This method does not set the "length" property of partially * applied functions. * * @static @@ -9307,7 +9507,7 @@ function now() { * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * - * **Note:** This method does not set the `length` property of partially + * **Note:** This method does not set the "length" property of partially * applied functions. * * @static @@ -9391,7 +9591,7 @@ function now() { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } - start = nativeMax(typeof start == 'undefined' ? (func.length - 1) : (+start || 0), 0); + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); return function() { var args = arguments, index = -1, @@ -9795,7 +9995,7 @@ function now() { if (value == null) { return true; } - var length = value.length; + var length = getLength(value); if (isLength(length) && (isArray(value) || isString(value) || isArguments(value) || (isObjectLike(value) && isFunction(value.splice)))) { return !length; @@ -9821,7 +10021,7 @@ function now() { * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparing values. + * @param {Function} [customizer] The function to customize value comparisons. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example @@ -9852,7 +10052,7 @@ function now() { return value === other; } var result = customizer ? customizer(value, other) : undefined; - return typeof result == 'undefined' ? baseIsEqual(value, other, customizer) : !!result; + return result === undefined ? baseIsEqual(value, other, customizer) : !!result; } /** @@ -9974,7 +10174,7 @@ function now() { * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize comparing values. + * @param {Function} [customizer] The function to customize value comparisons. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example @@ -10007,12 +10207,13 @@ function now() { return false; } customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 3); + object = toObject(object); if (!customizer && length == 1) { var key = props[0], value = source[key]; if (isStrictComparable(value)) { - return value === object[key] && (typeof value != 'undefined' || (key in toObject(object))); + return value === object[key] && (value !== undefined || (key in object)); } } var values = Array(length), @@ -10022,7 +10223,7 @@ function now() { value = values[length] = source[props[length]]; strictCompareFlags[length] = isStrictComparable(value); } - return baseIsMatch(toObject(object), props, values, strictCompareFlags, customizer); + return baseIsMatch(object, props, values, strictCompareFlags, customizer); } /** @@ -10077,9 +10278,9 @@ function now() { return false; } if (objToString.call(value) == funcTag) { - return reNative.test(fnToString.call(value)); + return reIsNative.test(fnToString.call(value)); } - return isObjectLike(value) && reHostCtor.test(value); + return isObjectLike(value) && reIsHostCtor.test(value); } /** @@ -10247,7 +10448,7 @@ function now() { * // => false */ function isUndefined(value) { - return typeof value == 'undefined'; + return value === undefined; } /** @@ -10266,7 +10467,7 @@ function now() { * // => [2, 3] */ function toArray(value) { - var length = value ? value.length : 0; + var length = value ? getLength(value) : 0; if (!isLength(length)) { return values(value); } @@ -10312,13 +10513,17 @@ function now() { * The `customizer` is bound to `thisArg` and invoked with five arguments: * (objectValue, sourceValue, key, object, source). * + * **Note:** This method mutates `object` and is based on + * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). + * + * * @static * @memberOf _ * @alias extend * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize assigning values. + * @param {Function} [customizer] The function to customize assigned values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {Object} Returns `object`. * @example @@ -10328,13 +10533,17 @@ function now() { * * // using a customizer callback * var defaults = _.partialRight(_.assign, function(value, other) { - * return typeof value == 'undefined' ? other : value; + * return _.isUndefined(value) ? other : value; * }); * * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); * // => { 'user': 'barney', 'age': 36 } */ - var assign = createAssigner(baseAssign); + var assign = createAssigner(function(object, source, customizer) { + return customizer + ? assignWith(object, source, customizer) + : baseAssign(object, source); + }); /** * Creates an object that inherits from the given `prototype` object. If a @@ -10375,7 +10584,7 @@ function now() { if (guard && isIterateeCall(prototype, properties, guard)) { properties = null; } - return properties ? baseCopy(properties, result, keys(properties)) : result; + return properties ? baseAssign(result, properties) : result; } /** @@ -10383,6 +10592,8 @@ function now() { * object for all destination properties that resolve to `undefined`. Once a * property is set, additional values of the same property are ignored. * + * **Note:** This method mutates `object`. + * * @static * @memberOf _ * @category Object @@ -10506,7 +10717,7 @@ function now() { /** * Iterates over own and inherited enumerable properties of an object invoking * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked - * with three arguments: (value, key, object). Iterator functions may exit + * with three arguments: (value, key, object). Iteratee functions may exit * iteration early by explicitly returning `false`. * * @static @@ -10562,7 +10773,7 @@ function now() { /** * Iterates over own enumerable properties of an object invoking `iteratee` * for each property. The `iteratee` is bound to `thisArg` and invoked with - * three arguments: (value, key, object). Iterator functions may exit iteration + * three arguments: (value, key, object). Iteratee functions may exit iteration * early by explicitly returning `false`. * * @static @@ -10635,24 +10846,68 @@ function now() { } /** - * Checks if `key` exists as a direct property of `object` instead of an - * inherited property. + * Gets the property value of `path` on `object`. If the resolved value is + * `undefined` the `defaultValue` is used in its place. * * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. - * @param {string} key The key to check. - * @returns {boolean} Returns `true` if `key` is a direct property, else `false`. + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. * @example * - * var object = { 'a': 1, 'b': 2, 'c': 3 }; + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * - * _.has(object, 'b'); + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, toPath(path), path + ''); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. + * @example + * + * var object = { 'a': { 'b': { 'c': 3 } } }; + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b.c'); + * // => true + * + * _.has(object, ['a', 'b', 'c']); * // => true */ - function has(object, key) { - return object ? hasOwnProperty.call(object, key) : false; + function has(object, path) { + if (object == null) { + return false; + } + var result = hasOwnProperty.call(object, path); + if (!result && !isKey(path)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + result = object != null && hasOwnProperty.call(object, path); + } + return result; } /** @@ -10715,7 +10970,7 @@ function now() { * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * @@ -10738,7 +10993,7 @@ function now() { length = object.length; } if ((typeof Ctor == 'function' && Ctor.prototype === object) || - (typeof object != 'function' && (length && isLength(length)))) { + (typeof object != 'function' && isLength(length))) { return shimKeys(object); } return isObject(object) ? nativeKeys(object) : []; @@ -10752,7 +11007,7 @@ function now() { * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * @@ -10860,7 +11115,7 @@ function now() { * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize merging properties. + * @param {Function} [customizer] The function to customize assigned values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {Object} Returns `object`. * @example @@ -10945,7 +11200,7 @@ function now() { * @static * @memberOf _ * @category Object - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Array} Returns the new array of key-value pairs. * @example * @@ -11001,41 +11256,93 @@ function now() { }); /** - * Resolves the value of property `key` on `object`. If the value of `key` is - * a function it is invoked with the `this` binding of `object` and its result - * is returned, else the property value is returned. If the property value is - * `undefined` the `defaultValue` is used in its place. + * This method is like `_.get` except that if the resolved value is a function + * it is invoked with the `this` binding of its parent object and its result + * is returned. * * @static * @memberOf _ * @category Object * @param {Object} object The object to query. - * @param {string} key The key of the property to resolve. - * @param {*} [defaultValue] The value returned if the property value - * resolves to `undefined`. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. * @returns {*} Returns the resolved value. * @example * - * var object = { 'user': 'fred', 'age': _.constant(40) }; + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; * - * _.result(object, 'user'); - * // => 'fred' + * _.result(object, 'a[0].b.c1'); + * // => 3 * - * _.result(object, 'age'); - * // => 40 + * _.result(object, 'a[0].b.c2'); + * // => 4 * - * _.result(object, 'status', 'busy'); - * // => 'busy' + * _.result(object, 'a.b.c', 'default'); + * // => 'default' * - * _.result(object, 'status', _.constant('busy')); - * // => 'busy' + * _.result(object, 'a.b.c', _.constant('default')); + * // => 'default' */ - function result(object, key, defaultValue) { - var value = object == null ? undefined : object[key]; - if (typeof value == 'undefined') { - value = defaultValue; + function result(object, path, defaultValue) { + var result = object == null ? undefined : object[path]; + if (result === undefined) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + result = object == null ? undefined : object[last(path)]; + } + result = result === undefined ? defaultValue : result; } - return isFunction(value) ? value.call(object) : value; + return isFunction(result) ? result.call(object) : result; + } + + /** + * Sets the property value of `path` on `object`. If a portion of `path` + * does not exist it is created. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to augment. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, 'x[0].y.z', 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + if (object == null) { + return object; + } + var pathKey = (path + ''); + path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path); + + var index = -1, + length = path.length, + endIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = path[index]; + if (isObject(nested)) { + if (index == endIndex) { + nested[key] = value; + } else if (nested[key] == null) { + nested[key] = isIndex(path[index + 1]) ? [] : {}; + } + } + nested = nested[key]; + } + return object; } /** @@ -11043,7 +11350,7 @@ function now() { * `accumulator` object which is the result of running each of its own enumerable * properties through `iteratee`, with each invocation potentially mutating * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked - * with four arguments: (accumulator, value, key, object). Iterator functions + * with four arguments: (accumulator, value, key, object). Iteratee functions * may exit iteration early by explicitly returning `false`. * * @static @@ -11186,7 +11493,7 @@ function now() { } else { end = +end || 0; } - return value >= start && value < end; + return value >= nativeMin(start, end) && value < nativeMax(start, end); } /** @@ -11311,7 +11618,7 @@ function now() { */ function deburr(string) { string = baseToString(string); - return string && string.replace(reLatin1, deburrLetter).replace(reComboMarks, ''); + return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, ''); } /** @@ -11340,7 +11647,7 @@ function now() { target = (target + ''); var length = string.length; - position = typeof position == 'undefined' + position = position === undefined ? length : nativeMin(position < 0 ? 0 : (+position || 0), length); @@ -11362,9 +11669,10 @@ function now() { * (under "semi-related fun fact") for more details. * * Backticks are escaped because in Internet Explorer < 9, they can break out - * of attribute values or HTML comments. See [#102](https://html5sec.org/#102), - * [#108](https://html5sec.org/#108), and [#133](https://html5sec.org/#133) of - * the [HTML5 Security Cheatsheet](https://html5sec.org/) for more details. + * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), + * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and + * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) + * for more details. * * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) * to reduce XSS vectors. @@ -11558,7 +11866,7 @@ function now() { radix = +radix; } string = trim(string); - return nativeParseInt(string, radix || (reHexPrefix.test(string) ? 16 : 10)); + return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10)); }; } @@ -11783,9 +12091,9 @@ function now() { options = otherOptions = null; } string = baseToString(string); - options = baseAssign(baseAssign({}, otherOptions || options), settings, assignOwnDefaults); + options = assignWith(baseAssign({}, otherOptions || options), settings, assignOwnDefaults); - var imports = baseAssign(baseAssign({}, options.imports), settings.imports, assignOwnDefaults), + var imports = assignWith(baseAssign({}, options.imports), settings.imports, assignOwnDefaults), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys); @@ -12189,9 +12497,7 @@ function now() { if (guard && isIterateeCall(func, thisArg, guard)) { thisArg = null; } - return isObjectLike(func) - ? matches(func) - : baseCallback(func, thisArg); + return baseCallback(func, thisArg); } /** @@ -12265,7 +12571,7 @@ function now() { } /** - * Creates a function which compares the property value of `key` on a given + * Creates a function which compares the property value of `path` on a given * object to `value`. * * **Note:** This method supports comparing arrays, booleans, `Date` objects, @@ -12275,7 +12581,7 @@ function now() { * @static * @memberOf _ * @category Utility - * @param {string} key The key of the property to get. + * @param {Array|string} path The path of the property to get. * @param {*} value The value to compare. * @returns {Function} Returns the new function. * @example @@ -12288,22 +12594,75 @@ function now() { * _.find(users, _.matchesProperty('user', 'fred')); * // => { 'user': 'fred' } */ - function matchesProperty(key, value) { - return baseMatchesProperty(key + '', baseClone(value, true)); + function matchesProperty(path, value) { + return baseMatchesProperty(path, baseClone(value, true)); } + /** + * Creates a function which invokes the method at `path` on a given object. + * + * @static + * @memberOf _ + * @category Utility + * @param {Array|string} path The path of the method to invoke. + * @returns {Function} Returns the new function. + * @example + * + * var objects = [ + * { 'a': { 'b': { 'c': _.constant(2) } } }, + * { 'a': { 'b': { 'c': _.constant(1) } } } + * ]; + * + * _.map(objects, _.method('a.b.c')); + * // => [2, 1] + * + * _.invoke(_.sortBy(objects, _.method(['a', 'b', 'c'])), 'a.b.c'); + * // => [1, 2] + */ + var method = restParam(function(path, args) { + return function(object) { + return invokePath(object, path, args); + } + }); + + /** + * The opposite of `_.method`; this method creates a function which invokes + * the method at a given path on `object`. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} object The object to query. + * @returns {Function} Returns the new function. + * @example + * + * var array = _.times(3, _.constant), + * object = { 'a': array, 'b': array, 'c': array }; + * + * _.map(['a[2]', 'c[0]'], _.methodOf(object)); + * // => [2, 0] + * + * _.map([['a', '2'], ['c', '0']], _.methodOf(object)); + * // => [2, 0] + */ + var methodOf = restParam(function(object, args) { + return function(path) { + return invokePath(object, path, args); + }; + }); + /** * Adds all own enumerable function properties of a source object to the * destination object. If `object` is a function then methods are added to * its prototype as well. * - * **Note:** Use `_.runInContext` to create a pristine `lodash` function - * for mixins to avoid conflicts caused by modifying the original. + * **Note:** Use `_.runInContext` to create a pristine `lodash` function to + * avoid conflicts caused by modifying the original. * * @static * @memberOf _ * @category Utility - * @param {Function|Object} [object=this] object The destination object. + * @param {Function|Object} [object=lodash] The destination object. * @param {Object} source The object of functions to add. * @param {Object} [options] The options object. * @param {boolean} [options.chain=true] Specify whether the functions added @@ -12420,61 +12779,61 @@ function now() { } /** - * Creates a function which returns the property value of `key` on a given object. + * Creates a function which returns the property value at `path` on a + * given object. * * @static * @memberOf _ * @category Utility - * @param {string} key The key of the property to get. + * @param {Array|string} path The path of the property to get. * @returns {Function} Returns the new function. * @example * - * var users = [ - * { 'user': 'fred' }, - * { 'user': 'barney' } + * var objects = [ + * { 'a': { 'b': { 'c': 2 } } }, + * { 'a': { 'b': { 'c': 1 } } } * ]; * - * var getName = _.property('user'); + * _.map(objects, _.property('a.b.c')); + * // => [2, 1] * - * _.map(users, getName); - * // => ['fred', 'barney'] - * - * _.pluck(_.sortBy(users, getName), 'user'); - * // => ['barney', 'fred'] + * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c'); + * // => [1, 2] */ - function property(key) { - return baseProperty(key + ''); + function property(path) { + return isKey(path) ? baseProperty(path) : basePropertyDeep(path); } /** * The opposite of `_.property`; this method creates a function which returns - * the property value of a given key on `object`. + * the property value at a given path on `object`. * * @static * @memberOf _ * @category Utility - * @param {Object} object The object to inspect. + * @param {Object} object The object to query. * @returns {Function} Returns the new function. * @example * - * var object = { 'a': 3, 'b': 1, 'c': 2 }; + * var array = [0, 1, 2], + * object = { 'a': array, 'b': array, 'c': array }; * - * _.map(['a', 'c'], _.propertyOf(object)); - * // => [3, 2] + * _.map(['a[2]', 'c[0]'], _.propertyOf(object)); + * // => [2, 0] * - * _.sortBy(['a', 'b', 'c'], _.propertyOf(object)); - * // => ['b', 'c', 'a'] + * _.map([['a', '2'], ['c', '0']], _.propertyOf(object)); + * // => [2, 0] */ function propertyOf(object) { - return function(key) { - return object == null ? undefined : object[key]; + return function(path) { + return baseGet(object, toPath(path), path + ''); }; } /** * Creates an array of numbers (positive and/or negative) progressing from * `start` up to, but not including, `end`. If `end` is not specified it is - * set to `start` with `start` then set to `0`. If `start` is less than `end` + * set to `start` with `start` then set to `0`. If `end` is less than `start` * a zero-length range is created unless a negative `step` is specified. * * @static @@ -12550,7 +12909,7 @@ function now() { * _.times(3, function(n) { * mage.castSpell(n); * }); - * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2` respectively + * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2` * * _.times(3, function(n) { * this.cast(n); @@ -12558,7 +12917,7 @@ function now() { * // => also invokes `mage.castSpell(n)` three times */ function times(n, iteratee, thisArg) { - n = +n; + n = floor(n); // Exit early to avoid a JSC JIT bug in Safari 8 // where `Array(0)` is treated as `Array(1)`. @@ -12617,7 +12976,7 @@ function now() { * // => 10 */ function add(augend, addend) { - return augend + addend; + return (+augend || 0) + (+addend || 0); } /** @@ -12843,6 +13202,8 @@ function now() { lodash.matchesProperty = matchesProperty; lodash.memoize = memoize; lodash.merge = merge; + lodash.method = method; + lodash.methodOf = methodOf; lodash.mixin = mixin; lodash.negate = negate; lodash.omit = omit; @@ -12863,6 +13224,7 @@ function now() { lodash.remove = remove; lodash.rest = rest; lodash.restParam = restParam; + lodash.set = set; lodash.shuffle = shuffle; lodash.slice = slice; lodash.sortBy = sortBy; @@ -12931,6 +13293,7 @@ function now() { lodash.findLastKey = findLastKey; lodash.findWhere = findWhere; lodash.first = first; + lodash.get = get; lodash.has = has; lodash.identity = identity; lodash.includes = includes; @@ -13119,7 +13482,7 @@ function now() { // Add `LazyWrapper` methods for `_.pluck` and `_.where`. arrayEach(['pluck', 'where'], function(methodName, index) { var operationName = index ? 'filter' : 'map', - createCallback = index ? baseMatches : baseProperty; + createCallback = index ? baseMatches : property; LazyWrapper.prototype[methodName] = function(value) { return this[operationName](createCallback(value)); @@ -13141,7 +13504,7 @@ function now() { start = start == null ? 0 : (+start || 0); var result = start < 0 ? this.takeRight(-start) : this.drop(start); - if (typeof end != 'undefined') { + if (end !== undefined) { end = (+end || 0); result = end < 0 ? result.dropRight(-end) : result.take(end - start); } @@ -13172,7 +13535,7 @@ function now() { useLazy = isLazy || isArray(value); if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) { - // avoid lazy use if the iteratee has a `length` other than `1` + // avoid lazy use if the iteratee has a "length" value other than `1` isLazy = useLazy = false; } var onlyLazy = isLazy && !isHybrid; @@ -13634,7 +13997,7 @@ Lexer.prototype = { } // 2-character punctuators: <= >= == != ++ -- << >> && || - // += -= *= %= &= |= ^= (but not /=, see below) + // += -= *= %= &= |= ^= /= if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { return { type: Token.Punctuator, @@ -13642,7 +14005,7 @@ Lexer.prototype = { }; } - if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { + if ("<>=!+-*%&|^/".indexOf(ch1) >= 0) { if (ch2 === "=") { return { type: Token.Punctuator, @@ -13656,22 +14019,6 @@ Lexer.prototype = { }; } - // Special case: /=. - - if (ch1 === "/") { - if (ch2 === "=") { - return { - type: Token.Punctuator, - value: "/=" - }; - } - - return { - type: Token.Punctuator, - value: "/" - }; - } - return null; }, @@ -13691,6 +14038,7 @@ Lexer.prototype = { var rest = this.input.substr(2); var startLine = this.line; var startChar = this.char; + var self = this; // Create a comment token object and make sure it // has all the data JSHint needs to work with special @@ -13709,6 +14057,11 @@ Lexer.prototype = { body = body.replace(/\n/g, " "); + if (label === "/*" && reg.fallsThrough.test(body)) { + isSpecial = true; + commentType = "falls through"; + } + special.forEach(function(str) { if (isSpecial) { return; @@ -13745,6 +14098,26 @@ Lexer.prototype = { commentType = "globals"; break; default: + var options = body.split(":").map(function(v) { + return v.replace(/^\s+/, "").replace(/\s+$/, ""); + }); + + if (options.length === 2) { + switch (options[0]) { + case "ignore": + switch (options[1]) { + case "start": + self.ignoringLinterErrors = true; + isSpecial = false; + break; + case "end": + self.ignoringLinterErrors = false; + isSpecial = false; + break; + } + } + } + commentType = str; } }); @@ -14059,12 +14432,12 @@ Lexer.prototype = { isAllowedDigit = isOctalDigit; base = 8; - if (!state.option.esnext) { + if (!state.inES6(true)) { this.trigger("warning", { code: "W119", line: this.line, character: this.char, - data: [ "Octal integer literal" ] + data: [ "Octal integer literal", "6" ] }); } @@ -14077,12 +14450,12 @@ Lexer.prototype = { isAllowedDigit = isBinaryDigit; base = 2; - if (!state.option.esnext) { + if (!state.inES6(true)) { this.trigger("warning", { code: "W119", line: this.line, character: this.char, - data: [ "Binary integer literal" ] + data: [ "Binary integer literal", "6" ] }); } @@ -14324,10 +14697,15 @@ Lexer.prototype = { var startChar = this.char; var depth = this.templateStarts.length; - if (!state.option.esnext) { - // Only lex template strings in ESNext mode. - return null; - } else if (this.peek() === "`") { + if (this.peek() === "`") { + if (!state.inES6(true)) { + this.trigger("warning", { + code: "W119", + line: this.line, + character: this.char, + data: ["template literal syntax", "6"] + }); + } // Template must start with a backtick. tokenType = Token.TemplateHead; this.templateStarts.push({ line: this.line, char: this.char }); @@ -14726,14 +15104,9 @@ Lexer.prototype = { this.from = this.char; // Move to the next non-space character. - var start; - if (/\s/.test(this.peek())) { - start = this.char; - - while (/\s/.test(this.peek())) { - this.from += 1; - this.skip(); - } + while (/\s/.test(this.peek())) { + this.from += 1; + this.skip(); } // Methods that work with multi-line structures and move the @@ -14798,7 +15171,7 @@ Lexer.prototype = { // If we are ignoring linter errors, replace the input with empty string // if it doesn't already at least start or end a multi-line comment - if (state.ignoreLinterErrors === true) { + if (this.ignoringLinterErrors === true) { if (!startsWith("/*", "//") && !(this.inComment && endsWith("*/"))) { this.input = ""; } @@ -14819,7 +15192,8 @@ Lexer.prototype = { // If there is a limit on line length, warn when lines get too // long. - if (state.option.maxlen && state.option.maxlen < this.input.length) { + if (!this.ignoringLinterErrors && state.option.maxlen && + state.option.maxlen < this.input.length) { var inComment = this.inComment || startsWith.call(inputTrimmed, "//") || startsWith.call(inputTrimmed, "/*"); @@ -14908,7 +15282,8 @@ Lexer.prototype = { } if (type === "(identifier)") { - if (value === "return" || value === "case" || value === "typeof") { + if (value === "return" || value === "case" || + value === "typeof" || value === "instanceof") { this.prereg = true; } @@ -14922,6 +15297,10 @@ Lexer.prototype = { } } + if (type === "(template)" || type === "(template middle)") { + this.prereg = true; + } + if (!obj) { obj = Object.create(state.syntax[type]); } @@ -15049,14 +15428,14 @@ Lexer.prototype = { return create("(no subst template)", token.value, null, token); case Token.Identifier: - this.trigger("Identifier", { + this.triggerAsync("Identifier", { line: this.line, char: this.char, - from: this.form, + from: this.from, name: token.value, raw_name: token.text, isProperty: state.tokens.curr.id === "." - }); + }, checks, function() { return true; }); /* falls through */ case Token.Keyword: @@ -15134,7 +15513,7 @@ Lexer.prototype = { exports.Lexer = Lexer; exports.Context = Context; -},{"../data/ascii-identifier-data.js":1,"../data/non-ascii-identifier-part-only.js":2,"../data/non-ascii-identifier-start.js":3,"./reg.js":17,"./state.js":18,"events":5,"lodash":12}],14:[function(require,module,exports){ +},{"../data/ascii-identifier-data.js":1,"../data/non-ascii-identifier-part-only.js":2,"../data/non-ascii-identifier-start.js":3,"./reg.js":17,"./state.js":19,"events":5,"lodash":12}],14:[function(require,module,exports){ "use strict"; var _ = require("lodash"); @@ -15157,7 +15536,7 @@ var errors = { E010: "'with' is not allowed in strict mode.", // Constants - E011: "const '{a}' has already been declared.", + E011: "'{a}' has already been declared.", E012: "const '{a}' is initialized to 'undefined'.", E013: "Attempting to override '{a}' which is a constant.", @@ -15199,15 +15578,19 @@ var errors = { E044: null, E045: "Invalid for each loop.", E046: "A yield statement shall be within a generator function (with syntax: `function*`)", - E047: null, // Vacant + E047: null, E048: "{a} declaration not directly within block.", E049: "A {a} cannot be named '{b}'.", E050: "Mozilla requires the yield expression to be parenthesized here.", - E051: "Regular parameters cannot come after default parameters.", + E051: null, E052: "Unclosed template literal.", E053: "Export declaration must be in global scope.", E054: "Class properties must be methods. Expected '(' but instead saw '{a}'.", - E055: "The '{a}' option cannot be set after any executable code." + E055: "The '{a}' option cannot be set after any executable code.", + E056: "'{a}' was used before it was declared, which is illegal for '{b}' variables.", + E057: "Invalid meta property: '{a}.{b}'.", + E058: "Missing semicolon.", + E059: "Incompatible values for the '{a}' and '{b}' linting options." }; var warnings = { @@ -15231,7 +15614,8 @@ var warnings = { W018: "Confusing use of '{a}'.", W019: "Use the isNaN function to compare with NaN.", W020: "Read only.", - W021: "'{a}' is a function.", + W021: "Reassignment of '{a}', which is is a {b}. " + + "Use 'var' or 'let' to declare bindings that may change.", W022: "Do not assign to the exception parameter.", W023: "Expected an identifier in an assignment and instead saw a function invocation.", W024: "Expected an identifier and instead saw '{a}' (a reserved word).", @@ -15305,7 +15689,7 @@ var warnings = { W089: "The body of a for in should be wrapped in an if statement to filter " + "unwanted properties from the prototype.", W090: "'{a}' is not a statement label.", - W091: "'{a}' is out of scope.", + W091: null, W093: "Did you mean to return a conditional instead of an assignment?", W094: "Unexpected comma.", W095: "Expected a string and instead saw {a}.", @@ -15317,7 +15701,7 @@ var warnings = { W101: "Line is too long.", W102: null, W103: "The '{a}' property is deprecated.", - W104: "'{a}' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).", + W104: "'{a}' is available in ES{b} (use 'esversion: {b}') or Mozilla JS extensions (use moz).", W105: "Unexpected {a} in '{b}'.", W106: "Identifier '{a}' is not in camel case.", W107: "Script URL.", @@ -15331,13 +15715,13 @@ var warnings = { W116: "Expected '{a}' and instead saw '{b}'.", W117: "'{a}' is not defined.", W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", - W119: "'{a}' is only available in ES6 (use esnext option).", + W119: "'{a}' is only available in ES{b} (use 'esversion: {b}').", W120: "You might be leaking a variable ({a}) here.", W121: "Extending prototype of native object: '{a}'.", W122: "Invalid typeof value '{a}'", W123: "'{a}' is already defined in outer scope.", W124: "A generator function shall contain a yield statement.", - W125: "This line contains non-breaking spaces: http://jshint.com/doc/options/#nonbsp", + W125: "This line contains non-breaking spaces: http://jshint.com/docs/options/#nonbsp", W126: "Unnecessary grouping operator.", W127: "Unexpected use of a comma operator.", W128: "Empty array elements require elision=true.", @@ -15347,7 +15731,11 @@ var warnings = { W131: "Invalid parameter after rest parameter.", W132: "`var` declarations are forbidden. Use `let` or `const` instead.", W133: "Invalid for-{a} loop left-hand-side: {b}.", - W134: "The '{a}' option is only available when linting ECMAScript {b} code." + W134: "The '{a}' option is only available when linting ECMAScript {b} code.", + W135: "{a} may not be supported by non-browser environments.", + W136: "'{a}' must be in function scope.", + W137: "Empty destructuring.", + W138: "Regular parameters should not come after default parameters." }; var info = { @@ -15542,6 +15930,8 @@ exports.bool = { * specification. Use this option if you need your program to be executable * in older browsers—such as Internet Explorer 6/7/8/9—and other legacy * JavaScript environments. + * + * @deprecated Use `esversion: 3` instead. */ es3 : true, @@ -15549,6 +15939,8 @@ exports.bool = { * This option enables syntax first defined in [the ECMAScript 5.1 * specification](http://es5.github.io/). This includes allowing reserved * keywords as object properties. + * + * @deprecated Use `esversion: 5` instead. */ es5 : true, @@ -15709,21 +16101,6 @@ exports.bool = { */ singleGroups: false, - /** - * This option requires all functions to run in ECMAScript 5's strict mode. - * [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) - * is a way to opt in to a restricted variant of JavaScript. Strict mode - * eliminates some JavaScript pitfalls that didn't cause errors by changing - * them to produce errors. It also fixes mistakes that made it difficult - * for the JavaScript engines to perform certain optimizations. - * - * *Note:* This option enables strict mode for function scope only. It - * *prohibits* the global scoped strict mode because it might break - * third-party widgets on your page. If you really want to use global - * strict mode, see the *globalstrict* option. - */ - strict : true, - /** * When set to true, the use of VariableStatements are forbidden. * For example: @@ -15826,6 +16203,8 @@ exports.bool = { * recommended. * * For more info about strict mode see the `strict` option. + * + * @deprecated Use `strict: "global"`. */ globalstrict: true, @@ -16001,13 +16380,14 @@ exports.bool = { /** * This option tells JSHint that your code uses ECMAScript 6 specific - * syntax. Note that these features are not finalized yet and not all - * browsers implement them. + * syntax. Note that not all browsers implement these features. * * More info: * - * * [Draft Specification for ES.next (ECMA-262 Ed. - * 6)](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts) + * * [Specification for ECMAScript + * 6](http://www.ecma-international.org/ecma-262/6.0/index.html) + * + * @deprecated Use `esversion: 6` instead. */ esnext : true, @@ -16208,7 +16588,18 @@ exports.val = { */ maxerr : false, - predef : false, // predef is deprecated and being replaced by globals + /** + * This option allows you to control which variables JSHint considers to be + * implicitly defined in the environment. Configure it with an array of + * string values. Prefixing a variable name with a hyphen (-) character will + * remove that name from the collection of predefined variables. + * + * JSHint will consider variables declared in this way to be read-only. + * + * This option cannot be specified in-line; it may only be used via the + * JavaScript API or from an external configuration file. + */ + predef : false, /** * This option can be used to specify a white list of global variables that @@ -16223,6 +16614,9 @@ exports.val = { * See also the "environment" options: a set of options to be used as short * hand for enabling global variables defined in common JavaScript * environments. + * + * To configure `globals` within an individual file, see [Inline + * Configuration](http://jshint.com/docs/#inline-configuration). */ globals : false, @@ -16322,6 +16716,25 @@ exports.val = { */ shadow : false, + /** + * This option requires the code to run in ECMAScript 5's strict mode. + * [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) + * is a way to opt in to a restricted variant of JavaScript. Strict mode + * eliminates some JavaScript pitfalls that didn't cause errors by changing + * them to produce errors. It also fixes mistakes that made it difficult + * for the JavaScript engines to perform certain optimizations. + * + * - "global" - there must be a `"use strict";` directive at global level + * - "implied" - lint the code as if there is the `"use strict";` directive + * - false - disable warnings about strict mode + * - true - there must be a `"use strict";` directive at function level; + * this is preferable for scripts intended to be loaded in web + * browsers directly because enabling strict mode globally + * could adversely effect other scripts running on the same + * page + */ + strict : true, + /** * This option warns when you define and never use your variables. It is very * useful for general code cleanup, especially when used in addition to @@ -16372,8 +16785,24 @@ exports.val = { // end - stop ignoring lines, starting on the next line // line - ignore warnings / errors for just a single line // (this option does not bypass the lexer) - ignoreDelimiters: false // array of start/end delimiters used to ignore - // certain chunks from code + + ignoreDelimiters: false, // array of start/end delimiters used to ignore + // certain chunks from code + + /** + * This option is used to specify the ECMAScript version to which the code + * must adhere. It can assume one of the following values: + * - `3` - If you need your program to be executable + * in older browsers—such as Internet Explorer 6/7/8/9—and other legacy + * JavaScript environments + * - `5` - To enable syntax first defined in [the ECMAScript 5.1 + * specification](http://www.ecma-international.org/ecma-262/5.1/index.html). + * This includes allowing reserved keywords as object properties. + * - `6` - To tell JSHint that your code uses [ECMAScript + * 6](http://www.ecma-international.org/ecma-262/6.0/index.html) specific + * syntax. Note that not all browsers implement them. + */ + esversion: 5 }; // These are JSHint boolean options which are shared with JSLint @@ -16456,7 +16885,7 @@ exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i; // Catches /* falls through */ comments (ft) -exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; +exports.fallsThrough = /^\s*falls?\sthrough\s*$/; // very conservative rule (eg: only one space between the start of the comment and the first character) // to relax the maxlen option @@ -16464,6 +16893,865 @@ exports.maxlenException = /^(?:(?:\/\/|\/\*|\*) ?)?[^ ]+$/; },{}],18:[function(require,module,exports){ "use strict"; + +var _ = require("lodash"); +var events = require("events"); + +// Used to denote membership in lookup tables (a primitive value such as `true` +// would be silently rejected for the property name "__proto__" in some +// environments) +var marker = {}; + +/** + * Creates a scope manager that handles variables and labels, storing usages + * and resolving when variables are used and undefined + */ +var scopeManager = function(state, predefined, exported, declared) { + + var _current; + var _scopeStack = []; + + function _newScope(type) { + _current = { + "(labels)": Object.create(null), + "(usages)": Object.create(null), + "(breakLabels)": Object.create(null), + "(parent)": _current, + "(type)": type, + "(params)": (type === "functionparams" || type === "catchparams") ? [] : null + }; + _scopeStack.push(_current); + } + + _newScope("global"); + _current["(predefined)"] = predefined; + + var _currentFunctBody = _current; // this is the block after the params = function + + var usedPredefinedAndGlobals = Object.create(null); + var impliedGlobals = Object.create(null); + var unuseds = []; + var emitter = new events.EventEmitter(); + + function warning(code, token) { + emitter.emit("warning", { + code: code, + token: token, + data: _.slice(arguments, 2) + }); + } + + function error(code, token) { + emitter.emit("warning", { + code: code, + token: token, + data: _.slice(arguments, 2) + }); + } + + function _setupUsages(labelName) { + if (!_current["(usages)"][labelName]) { + _current["(usages)"][labelName] = { + "(modified)": [], + "(reassigned)": [], + "(tokens)": [] + }; + } + } + + var _getUnusedOption = function(unused_opt) { + if (unused_opt === undefined) { + unused_opt = state.option.unused; + } + + if (unused_opt === true) { + unused_opt = "last-param"; + } + + return unused_opt; + }; + + var _warnUnused = function(name, tkn, type, unused_opt) { + var line = tkn.line; + var chr = tkn.from; + var raw_name = tkn.raw_text || name; + + unused_opt = _getUnusedOption(unused_opt); + + var warnable_types = { + "vars": ["var"], + "last-param": ["var", "param"], + "strict": ["var", "param", "last-param"] + }; + + if (unused_opt) { + if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { + warning("W098", { line: line, from: chr }, raw_name); + } + } + + // inconsistent - see gh-1894 + if (unused_opt || type === "var") { + unuseds.push({ + name: name, + line: line, + character: chr + }); + } + }; + + /** + * Checks the current scope for unused identifiers + */ + function _checkForUnused() { + // function params are handled specially + // assume that parameters are the only thing declared in the param scope + if (_current["(type)"] === "functionparams") { + _checkParams(); + return; + } + var curentLabels = _current["(labels)"]; + for (var labelName in curentLabels) { + if (curentLabels[labelName]) { + if (curentLabels[labelName]["(type)"] !== "exception" && + curentLabels[labelName]["(unused)"]) { + _warnUnused(labelName, curentLabels[labelName]["(token)"], "var"); + } + } + } + } + + /** + * Checks the current scope for unused parameters + * Must be called in a function parameter scope + */ + function _checkParams() { + var params = _current["(params)"]; + + if (!params) { + return; + } + + var param = params.pop(); + var unused_opt; + + while (param) { + var label = _current["(labels)"][param]; + + unused_opt = _getUnusedOption(state.funct["(unusedOption)"]); + + // 'undefined' is a special case for (function(window, undefined) { ... })(); + // patterns. + if (param === "undefined") + return; + + if (label["(unused)"]) { + _warnUnused(param, label["(token)"], "param", state.funct["(unusedOption)"]); + } else if (unused_opt === "last-param") { + return; + } + + param = params.pop(); + } + } + + /** + * Finds the relevant label's scope, searching from nearest outwards + * @returns {Object} the scope the label was found in + */ + function _getLabel(labelName) { + for (var i = _scopeStack.length - 1 ; i >= 0; --i) { + var scopeLabels = _scopeStack[i]["(labels)"]; + if (scopeLabels[labelName]) { + return scopeLabels; + } + } + } + + function usedSoFarInCurrentFunction(labelName) { + // used so far in this whole function and any sub functions + for (var i = _scopeStack.length - 1; i >= 0; i--) { + var current = _scopeStack[i]; + if (current["(usages)"][labelName]) { + return current["(usages)"][labelName]; + } + if (current === _currentFunctBody) { + break; + } + } + return false; + } + + function _checkOuterShadow(labelName, token) { + + // only check if shadow is outer + if (state.option.shadow !== "outer") { + return; + } + + var isGlobal = _currentFunctBody["(type)"] === "global", + isNewFunction = _current["(type)"] === "functionparams"; + + var outsideCurrentFunction = !isGlobal; + for (var i = 0; i < _scopeStack.length; i++) { + var stackItem = _scopeStack[i]; + + if (!isNewFunction && _scopeStack[i + 1] === _currentFunctBody) { + outsideCurrentFunction = false; + } + if (outsideCurrentFunction && stackItem["(labels)"][labelName]) { + warning("W123", token, labelName); + } + if (stackItem["(breakLabels)"][labelName]) { + warning("W123", token, labelName); + } + } + } + + function _latedefWarning(type, labelName, token) { + if (state.option.latedef) { + // if either latedef is strict and this is a function + // or this is not a function + if ((state.option.latedef === true && type === "function") || + type !== "function") { + warning("W003", token, labelName); + } + } + } + + var scopeManagerInst = { + + on: function(names, listener) { + names.split(" ").forEach(function(name) { + emitter.on(name, listener); + }); + }, + + isPredefined: function(labelName) { + return !this.has(labelName) && _.has(_scopeStack[0]["(predefined)"], labelName); + }, + + /** + * Tell the manager we are entering a new block of code + * @param {string} [type] - The type of the block. Valid values are + * "functionparams", "catchparams" and + * "functionouter" + */ + stack: function(type) { + var previousScope = _current; + _newScope(type); + + if (!type && previousScope["(type)"] === "functionparams") { + + _current["(isFuncBody)"] = true; + _current["(context)"] = _currentFunctBody; + _currentFunctBody = _current; + } + }, + + unstack: function() { + // jshint proto: true + var subScope = _scopeStack.length > 1 ? _scopeStack[_scopeStack.length - 2] : null; + var isUnstackingFunctionBody = _current === _currentFunctBody, + isUnstackingFunctionParams = _current["(type)"] === "functionparams", + isUnstackingFunctionOuter = _current["(type)"] === "functionouter"; + + var i, j; + var currentUsages = _current["(usages)"]; + var currentLabels = _current["(labels)"]; + var usedLabelNameList = Object.keys(currentUsages); + + if (currentUsages.__proto__ && usedLabelNameList.indexOf("__proto__") === -1) { + usedLabelNameList.push("__proto__"); + } + + for (i = 0; i < usedLabelNameList.length; i++) { + var usedLabelName = usedLabelNameList[i]; + + var usage = currentUsages[usedLabelName]; + var usedLabel = currentLabels[usedLabelName]; + if (usedLabel) { + var usedLabelType = usedLabel["(type)"]; + + if (usedLabel["(useOutsideOfScope)"] && !state.option.funcscope) { + var usedTokens = usage["(tokens)"]; + if (usedTokens) { + for (j = 0; j < usedTokens.length; j++) { + // Keep the consistency of https://github.com/jshint/jshint/issues/2409 + if (usedLabel["(function)"] === usedTokens[j]["(function)"]) { + error("W038", usedTokens[j], usedLabelName); + } + } + } + } + + // mark the label used + _current["(labels)"][usedLabelName]["(unused)"] = false; + + // check for modifying a const + if (usedLabelType === "const" && usage["(modified)"]) { + for (j = 0; j < usage["(modified)"].length; j++) { + error("E013", usage["(modified)"][j], usedLabelName); + } + } + + // check for re-assigning a function declaration + if ((usedLabelType === "function" || usedLabelType === "class") && + usage["(reassigned)"]) { + for (j = 0; j < usage["(reassigned)"].length; j++) { + if (!usage["(reassigned)"][j].ignoreW021) { + warning("W021", usage["(reassigned)"][j], usedLabelName, usedLabelType); + } + } + } + continue; + } + + if (isUnstackingFunctionOuter) { + state.funct["(isCapturing)"] = true; + } + + if (subScope) { + // not exiting the global scope, so copy the usage down in case its an out of scope usage + if (!subScope["(usages)"][usedLabelName]) { + subScope["(usages)"][usedLabelName] = usage; + if (isUnstackingFunctionBody) { + subScope["(usages)"][usedLabelName]["(onlyUsedSubFunction)"] = true; + } + } else { + var subScopeUsage = subScope["(usages)"][usedLabelName]; + subScopeUsage["(modified)"] = subScopeUsage["(modified)"].concat(usage["(modified)"]); + subScopeUsage["(tokens)"] = subScopeUsage["(tokens)"].concat(usage["(tokens)"]); + subScopeUsage["(reassigned)"] = + subScopeUsage["(reassigned)"].concat(usage["(reassigned)"]); + } + } else { + // this is exiting global scope, so we finalise everything here - we are at the end of the file + if (typeof _current["(predefined)"][usedLabelName] === "boolean") { + + // remove the declared token, so we know it is used + delete declared[usedLabelName]; + + // note it as used so it can be reported + usedPredefinedAndGlobals[usedLabelName] = marker; + + // check for re-assigning a read-only (set to false) predefined + if (_current["(predefined)"][usedLabelName] === false && usage["(reassigned)"]) { + for (j = 0; j < usage["(reassigned)"].length; j++) { + if (!usage["(reassigned)"][j].ignoreW020) { + warning("W020", usage["(reassigned)"][j]); + } + } + } + } + else { + // label usage is not predefined and we have not found a declaration + // so report as undeclared + if (usage["(tokens)"]) { + for (j = 0; j < usage["(tokens)"].length; j++) { + var undefinedToken = usage["(tokens)"][j]; + // if its not a forgiven undefined (e.g. typof x) + if (!undefinedToken.forgiveUndef) { + // if undef is on and undef was on when the token was defined + if (state.option.undef && !undefinedToken.ignoreUndef) { + warning("W117", undefinedToken, usedLabelName); + } + if (impliedGlobals[usedLabelName]) { + impliedGlobals[usedLabelName].line.push(undefinedToken.line); + } else { + impliedGlobals[usedLabelName] = { + name: usedLabelName, + line: [undefinedToken.line] + }; + } + } + } + } + } + } + } + + // if exiting the global scope, we can warn about declared globals that haven't been used yet + if (!subScope) { + Object.keys(declared) + .forEach(function(labelNotUsed) { + _warnUnused(labelNotUsed, declared[labelNotUsed], "var"); + }); + } + + // If this is not a function boundary, transfer function-scoped labels to + // the parent block (a rough simulation of variable hoisting). Previously + // existing labels in the parent block should take precedence so that things and stuff. + if (subScope && !isUnstackingFunctionBody && + !isUnstackingFunctionParams && !isUnstackingFunctionOuter) { + var labelNames = Object.keys(currentLabels); + for (i = 0; i < labelNames.length; i++) { + + var defLabelName = labelNames[i]; + var defLabel = currentLabels[defLabelName]; + + if (!defLabel["(blockscoped)"] && defLabel["(type)"] !== "exception") { + var shadowed = subScope["(labels)"][defLabelName]; + + // Do not overwrite a label if it exists in the parent scope + // because it is shared by adjacent blocks. Copy the `unused` + // property so that any references found within the current block + // are counted toward that higher-level declaration. + if (shadowed) { + shadowed["(unused)"] &= defLabel["(unused)"]; + + // "Hoist" the variable to the parent block, decorating the label + // so that future references, though technically valid, can be + // reported as "out-of-scope" in the absence of the `funcscope` + // option. + } else { + defLabel["(useOutsideOfScope)"] = + // Do not warn about out-of-scope usages in the global scope + _currentFunctBody["(type)"] !== "global" && + // When a higher scope contains a binding for the label, the + // label is a re-declaration and should not prompt "used + // out-of-scope" warnings. + !this.funct.has(defLabelName, { excludeCurrent: true }); + + subScope["(labels)"][defLabelName] = defLabel; + } + + delete currentLabels[defLabelName]; + } + } + } + + _checkForUnused(); + + _scopeStack.pop(); + if (isUnstackingFunctionBody) { + _currentFunctBody = _scopeStack[_.findLastIndex(_scopeStack, function(scope) { + // if function or if global (which is at the bottom so it will only return true if we call back) + return scope["(isFuncBody)"] || scope["(type)"] === "global"; + })]; + } + + _current = subScope; + }, + + /** + * Add a param to the current scope + * @param {string} labelName + * @param {Token} token + * @param {string} [type="param"] param type + */ + addParam: function(labelName, token, type) { + type = type || "param"; + + if (type === "exception") { + // if defined in the current function + var previouslyDefinedLabelType = this.funct.labeltype(labelName); + if (previouslyDefinedLabelType && previouslyDefinedLabelType !== "exception") { + // and has not been used yet in the current function scope + if (!state.option.node) { + warning("W002", state.tokens.next, labelName); + } + } + } + + // The variable was declared in the current scope + if (_.has(_current["(labels)"], labelName)) { + _current["(labels)"][labelName].duplicated = true; + + // The variable was declared in an outer scope + } else { + // if this scope has the variable defined, it's a re-definition error + _checkOuterShadow(labelName, token, type); + + _current["(labels)"][labelName] = { + "(type)" : type, + "(token)": token, + "(unused)": true }; + + _current["(params)"].push(labelName); + } + + if (_.has(_current["(usages)"], labelName)) { + var usage = _current["(usages)"][labelName]; + // if its in a sub function it is not necessarily an error, just latedef + if (usage["(onlyUsedSubFunction)"]) { + _latedefWarning(type, labelName, token); + } else { + // this is a clear illegal usage for block scoped variables + warning("E056", token, labelName, type); + } + } + }, + + validateParams: function() { + // This method only concerns errors for function parameters + if (_currentFunctBody["(type)"] === "global") { + return; + } + + var isStrict = state.isStrict(); + var currentFunctParamScope = _currentFunctBody["(parent)"]; + + if (!currentFunctParamScope["(params)"]) { + return; + } + + currentFunctParamScope["(params)"].forEach(function(labelName) { + var label = currentFunctParamScope["(labels)"][labelName]; + + if (label && label.duplicated) { + if (isStrict) { + warning("E011", label["(token)"], labelName); + } else if (state.option.shadow !== true) { + warning("W004", label["(token)"], labelName); + } + } + }); + }, + + getUsedOrDefinedGlobals: function() { + // jshint proto: true + var list = Object.keys(usedPredefinedAndGlobals); + + // If `__proto__` is used as a global variable name, its entry in the + // lookup table may not be enumerated by `Object.keys` (depending on the + // environment). + if (usedPredefinedAndGlobals.__proto__ === marker && + list.indexOf("__proto__") === -1) { + list.push("__proto__"); + } + + return list; + }, + + /** + * Gets an array of implied globals + * @returns {Array.<{ name: string, line: Array.}>} + */ + getImpliedGlobals: function() { + // jshint proto: true + var values = _.values(impliedGlobals); + var hasProto = false; + + // If `__proto__` is an implied global variable, its entry in the lookup + // table may not be enumerated by `_.values` (depending on the + // environment). + if (impliedGlobals.__proto__) { + hasProto = values.some(function(value) { + return value.name === "__proto__"; + }); + + if (!hasProto) { + values.push(impliedGlobals.__proto__); + } + } + + return values; + }, + + /** + * Returns a list of unused variables + * @returns {Array} + */ + getUnuseds: function() { + return unuseds; + }, + + has: function(labelName) { + return Boolean(_getLabel(labelName)); + }, + + labeltype: function(labelName) { + // returns a labels type or null if not present + var scopeLabels = _getLabel(labelName); + if (scopeLabels) { + return scopeLabels[labelName]["(type)"]; + } + return null; + }, + + /** + * for the exported options, indicating a variable is used outside the file + */ + addExported: function(labelName) { + var globalLabels = _scopeStack[0]["(labels)"]; + if (_.has(declared, labelName)) { + // remove the declared token, so we know it is used + delete declared[labelName]; + } else if (_.has(globalLabels, labelName)) { + globalLabels[labelName]["(unused)"] = false; + } else { + for (var i = 1; i < _scopeStack.length; i++) { + var scope = _scopeStack[i]; + // if `scope.(type)` is not defined, it is a block scope + if (!scope["(type)"]) { + if (_.has(scope["(labels)"], labelName) && + !scope["(labels)"][labelName]["(blockscoped)"]) { + scope["(labels)"][labelName]["(unused)"] = false; + return; + } + } else { + break; + } + } + exported[labelName] = true; + } + }, + + /** + * Mark an indentifier as es6 module exported + */ + setExported: function(labelName, token) { + this.block.use(labelName, token); + }, + + /** + * adds an indentifier to the relevant current scope and creates warnings/errors as necessary + * @param {string} labelName + * @param {Object} opts + * @param {String} opts.type - the type of the label e.g. "param", "var", "let, "const", "function" + * @param {Token} opts.token - the token pointing at the declaration + */ + addlabel: function(labelName, opts) { + + var type = opts.type; + var token = opts.token; + var isblockscoped = type === "let" || type === "const" || type === "class"; + var isexported = (isblockscoped ? _current : _currentFunctBody)["(type)"] === "global" && + _.has(exported, labelName); + + // outer shadow check (inner is only on non-block scoped) + _checkOuterShadow(labelName, token, type); + + // if is block scoped (let or const) + if (isblockscoped) { + + var declaredInCurrentScope = _current["(labels)"][labelName]; + // for block scoped variables, params are seen in the current scope as the root function + // scope, so check these too. + if (!declaredInCurrentScope && _current === _currentFunctBody && + _current["(type)"] !== "global") { + declaredInCurrentScope = !!_currentFunctBody["(parent)"]["(labels)"][labelName]; + } + + // if its not already defined (which is an error, so ignore) and is used in TDZ + if (!declaredInCurrentScope && _current["(usages)"][labelName]) { + var usage = _current["(usages)"][labelName]; + // if its in a sub function it is not necessarily an error, just latedef + if (usage["(onlyUsedSubFunction)"]) { + _latedefWarning(type, labelName, token); + } else { + // this is a clear illegal usage for block scoped variables + warning("E056", token, labelName, type); + } + } + + // if this scope has the variable defined, its a re-definition error + if (declaredInCurrentScope) { + warning("E011", token, labelName); + } + else if (state.option.shadow === "outer") { + + // if shadow is outer, for block scope we want to detect any shadowing within this function + if (scopeManagerInst.funct.has(labelName)) { + warning("W004", token, labelName); + } + } + + scopeManagerInst.block.add(labelName, type, token, !isexported); + + } else { + + var declaredInCurrentFunctionScope = scopeManagerInst.funct.has(labelName); + + // check for late definition, ignore if already declared + if (!declaredInCurrentFunctionScope && usedSoFarInCurrentFunction(labelName)) { + _latedefWarning(type, labelName, token); + } + + // defining with a var or a function when a block scope variable of the same name + // is in scope is an error + if (scopeManagerInst.funct.has(labelName, { onlyBlockscoped: true })) { + warning("E011", token, labelName); + } else if (state.option.shadow !== true) { + // now since we didn't get any block scope variables, test for var/function + // shadowing + if (declaredInCurrentFunctionScope && labelName !== "__proto__") { + + // see https://github.com/jshint/jshint/issues/2400 + if (_currentFunctBody["(type)"] !== "global") { + warning("W004", token, labelName); + } + } + } + + scopeManagerInst.funct.add(labelName, type, token, !isexported); + + if (_currentFunctBody["(type)"] === "global" && !state.impliedClosure()) { + usedPredefinedAndGlobals[labelName] = marker; + } + } + }, + + funct: { + /** + * Returns the label type given certain options + * @param labelName + * @param {Object=} options + * @param {Boolean=} options.onlyBlockscoped - only include block scoped labels + * @param {Boolean=} options.excludeParams - exclude the param scope + * @param {Boolean=} options.excludeCurrent - exclude the current scope + * @returns {String} + */ + labeltype: function(labelName, options) { + var onlyBlockscoped = options && options.onlyBlockscoped; + var excludeParams = options && options.excludeParams; + var currentScopeIndex = _scopeStack.length - (options && options.excludeCurrent ? 2 : 1); + for (var i = currentScopeIndex; i >= 0; i--) { + var current = _scopeStack[i]; + if (current["(labels)"][labelName] && + (!onlyBlockscoped || current["(labels)"][labelName]["(blockscoped)"])) { + return current["(labels)"][labelName]["(type)"]; + } + var scopeCheck = excludeParams ? _scopeStack[ i - 1 ] : current; + if (scopeCheck && scopeCheck["(type)"] === "functionparams") { + return null; + } + } + return null; + }, + /** + * Returns if a break label exists in the function scope + * @param {string} labelName + * @returns {boolean} + */ + hasBreakLabel: function(labelName) { + for (var i = _scopeStack.length - 1; i >= 0; i--) { + var current = _scopeStack[i]; + + if (current["(breakLabels)"][labelName]) { + return true; + } + if (current["(type)"] === "functionparams") { + return false; + } + } + return false; + }, + /** + * Returns if the label is in the current function scope + * See scopeManager.funct.labelType for options + */ + has: function(labelName, options) { + return Boolean(this.labeltype(labelName, options)); + }, + + /** + * Adds a new function scoped variable + * see block.add for block scoped + */ + add: function(labelName, type, tok, unused) { + _current["(labels)"][labelName] = { + "(type)" : type, + "(token)": tok, + "(blockscoped)": false, + "(function)": _currentFunctBody, + "(unused)": unused }; + } + }, + + block: { + + /** + * is the current block global? + * @returns Boolean + */ + isGlobal: function() { + return _current["(type)"] === "global"; + }, + + use: function(labelName, token) { + + // if resolves to current function params, then do not store usage just resolve + // this is because function(a) { var a; a = a; } will resolve to the param, not + // to the unset var + // first check the param is used + var paramScope = _currentFunctBody["(parent)"]; + if (paramScope && paramScope["(labels)"][labelName] && + paramScope["(labels)"][labelName]["(type)"] === "param") { + + // then check its not declared by a block scope variable + if (!scopeManagerInst.funct.has(labelName, + { excludeParams: true, onlyBlockscoped: true })) { + paramScope["(labels)"][labelName]["(unused)"] = false; + } + } + + if (token && (state.ignored.W117 || state.option.undef === false)) { + token.ignoreUndef = true; + } + + _setupUsages(labelName); + + _current["(usages)"][labelName]["(onlyUsedSubFunction)"] = false; + + if (token) { + token["(function)"] = _currentFunctBody; + _current["(usages)"][labelName]["(tokens)"].push(token); + } + }, + + reassign: function(labelName, token) { + token.ignoreW020 = state.ignored.W020; + token.ignoreW021 = state.ignored.W021; + + this.modify(labelName, token); + + _current["(usages)"][labelName]["(reassigned)"].push(token); + }, + + modify: function(labelName, token) { + + _setupUsages(labelName); + + _current["(usages)"][labelName]["(onlyUsedSubFunction)"] = false; + _current["(usages)"][labelName]["(modified)"].push(token); + }, + + /** + * Adds a new variable + */ + add: function(labelName, type, tok, unused) { + _current["(labels)"][labelName] = { + "(type)" : type, + "(token)": tok, + "(blockscoped)": true, + "(unused)": unused }; + }, + + addBreakLabel: function(labelName, opts) { + var token = opts.token; + if (scopeManagerInst.funct.hasBreakLabel(labelName)) { + warning("E011", token, labelName); + } + else if (state.option.shadow === "outer") { + if (scopeManagerInst.funct.has(labelName)) { + warning("W004", token, labelName); + } else { + _checkOuterShadow(labelName, token); + } + } + _current["(breakLabels)"][labelName] = token; + } + } + }; + return scopeManagerInst; +}; + +module.exports = scopeManager; + +},{"events":5,"lodash":12}],19:[function(require,module,exports){ +"use strict"; var NameStack = require("./name-stack.js"); var state = { @@ -16476,34 +17764,117 @@ var state = { */ isStrict: function() { return this.directive["use strict"] || this.inClassBody || - this.option.module; - }, - - // Assumption: chronologically ES3 < ES5 < ES6/ESNext < Moz - inMoz: function() { - return this.option.moz && !this.option.esnext; + this.option.module || this.option.strict === "implied"; }, /** - * @param {object} options - * @param {boolean} options.strict - When `true`, only consider ESNext when - * in "esnext" code and *not* in "moz". + * Determine if the current state warrants a warning for statements outside + * of strict mode code. + * + * While emitting warnings based on function scope would be more intuitive + * (and less noisy), JSHint observes statement-based semantics in order to + * preserve legacy behavior. + * + * This method does not take the state of the parser into account, making no + * distinction between global code and function code. Because the "missing + * 'use strict'" warning is *also* reported at function boundaries, this + * function interprets `strict` option values `true` and `undefined` as + * equivalent. */ - inESNext: function(strict) { - if (strict) { - return !this.option.moz && this.option.esnext; + stmtMissingStrict: function() { + if (this.option.strict === "global") { + return true; } - return this.option.moz || this.option.esnext; + + if (this.option.strict === false) { + return false; + } + + if (this.option.globalstrict) { + return true; + } + + return false; }, - inES5: function() { - return !this.option.es3; + allowsGlobalUsd: function() { + return this.option.strict === "global" || this.option.globalstrict || + this.option.module || this.impliedClosure(); }, - inES3: function() { - return this.option.es3; + /** + * Determine if the current configuration describes an environment that is + * wrapped in an immediately-invoked function expression prior to evaluation. + * + * @returns {boolean} + */ + impliedClosure: function() { + return this.option.node || this.option.phantom || this.option.browserify; }, + // Assumption: chronologically ES3 < ES5 < ES6 < Moz + + inMoz: function() { + return this.option.moz; + }, + + /** + * @param {boolean} strict - When `true`, only consider ES6 when in + * "esversion: 6" code. + */ + inES6: function(strict) { + if (strict) { + return this.esVersion === 6; + } + return this.option.moz || this.esVersion >= 6; + }, + + /** + * @param {boolean} strict - When `true`, return `true` only when + * esVersion is exactly 5 + */ + inES5: function(strict) { + if (strict) { + return (!this.esVersion || this.esVersion === 5) && !this.option.moz; + } + return !this.esVersion || this.esVersion >= 5 || this.option.moz; + }, + + /** + * Determine the current version of the input language by inspecting the + * value of all ECMAScript-version-related options. This logic is necessary + * to ensure compatibility with deprecated options `es3`, `es5`, and + * `esnext`, and it may be drastically simplified when those options are + * removed. + * + * @returns {string|null} - the name of any incompatible option detected, + * `null` otherwise + */ + inferEsVersion: function() { + var badOpt = null; + + if (this.option.esversion) { + if (this.option.es3) { + badOpt = "es3"; + } else if (this.option.es5) { + badOpt = "es5"; + } else if (this.option.esnext) { + badOpt = "esnext"; + } + + if (badOpt) { + return badOpt; + } + + this.esVersion = this.option.esversion; + } else if (this.option.es3) { + this.esVersion = 3; + } else if (this.option.esnext) { + this.esVersion = 6; + } + + return null; + }, reset: function() { this.tokens = { @@ -16513,6 +17884,8 @@ var state = { }; this.option = {}; + this.esVersion = 5; + this.funct = null; this.ignored = {}; this.directive = {}; this.jsonMode = false; @@ -16524,15 +17897,12 @@ var state = { this.forinifcheckneeded = false; this.nameStack = new NameStack(); this.inClassBody = false; - - // Blank out non-multi-line-commented lines when ignoring linter errors - this.ignoreLinterErrors = false; } }; exports.state = state; -},{"./name-stack.js":15}],19:[function(require,module,exports){ +},{"./name-stack.js":15}],20:[function(require,module,exports){ "use strict"; exports.register = function(linter) { @@ -16548,13 +17918,14 @@ exports.register = function(linter) { linter.warn("W103", { line: data.line, char: data.char, - data: [ data.name ] + data: [ data.name, "6" ] }); } }); // Check for properties named __iterator__. This is a special property - // available only in browsers with JavaScript 1.7 implementation. + // available only in browsers with JavaScript 1.7 implementation, but + // it is deprecated for ES6 linter.on("Identifier", function style_scanIterator(data) { if (linter.getOption("iterator")) { @@ -16562,7 +17933,7 @@ exports.register = function(linter) { } if (data.name === "__iterator__") { - linter.warn("W104", { + linter.warn("W103", { line: data.line, char: data.char, data: [ data.name ] @@ -16581,7 +17952,7 @@ exports.register = function(linter) { if (data.name.replace(/^_+|_+$/g, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { linter.warn("W106", { line: data.line, - char: data.from, + char: data.char, data: [ data.name ] }); } @@ -16677,7 +18048,7 @@ exports.register = function(linter) { }); }; -},{}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ // jshint -W001 "use strict"; @@ -16753,6 +18124,7 @@ exports.browser = { close : false, closed : false, Comment : false, + CompositionEvent : false, CustomEvent : false, DOMParser : false, defaultStatus : false, @@ -16764,6 +18136,8 @@ exports.browser = { Event : false, event : false, fetch : false, + File : false, + FileList : false, FileReader : false, FormData : false, focus : false, @@ -16777,6 +18151,7 @@ exports.browser = { HTMLBRElement : false, HTMLButtonElement : false, HTMLCanvasElement : false, + HTMLCollection : false, HTMLDirectoryElement : false, HTMLDivElement : false, HTMLDListElement : false, @@ -16856,6 +18231,7 @@ exports.browser = { opener : false, Option : false, parent : false, + performance : false, print : false, Range : false, requestAnimationFrame : false, @@ -17045,6 +18421,7 @@ exports.browser = { WebGLUniformLocation : false, WebSocket : false, window : false, + Window : false, Worker : false, XDomainRequest : false, XMLHttpRequest : false, @@ -17145,6 +18522,7 @@ exports.qunit = { module : false, notDeepEqual : false, notEqual : false, + notOk : false, notPropEqual : false, notStrictEqual : false, ok : false, @@ -17350,6 +18728,8 @@ exports.yui = { }; exports.mocha = { + // Global (for config etc.) + mocha : false, // BDD describe : false, xdescribe : false, @@ -17391,7 +18771,8 @@ exports.jasmine = { afterAll : false, fail : false, fdescribe : false, - fit : false + fit : false, + pending : false }; },{}],"jshint":[function(require,module,exports){ @@ -17429,15 +18810,16 @@ exports.jasmine = { /*global console:true */ /*exported console */ -var _ = require("lodash"); -var events = require("events"); -var vars = require("./vars.js"); -var messages = require("./messages.js"); -var Lexer = require("./lex.js").Lexer; -var reg = require("./reg.js"); -var state = require("./state.js").state; -var style = require("./style.js"); -var options = require("./options.js"); +var _ = require("lodash"); +var events = require("events"); +var vars = require("./vars.js"); +var messages = require("./messages.js"); +var Lexer = require("./lex.js").Lexer; +var reg = require("./reg.js"); +var state = require("./state.js").state; +var style = require("./style.js"); +var options = require("./options.js"); +var scopeManager = require("./scope-manager.js"); // We need this module here because environments such as IE and Rhino // don't necessarilly expose the 'console' API and browserify uses @@ -17471,18 +18853,14 @@ var JSHINT = (function() { }, declared, // Globals that were declared using /*global ... */ syntax. - exported, // Variables that are used outside of the current file. functionicity = [ "closure", "exception", "global", "label", "outer", "unused", "var" ], - funct, // The current function functions, // All of the functions - global, // The global scope - implied, // Implied globals inblock, indent, lookahead, @@ -17491,9 +18869,7 @@ var JSHINT = (function() { membersOnly, predefined, // Global variables defined by option - scope, // The current scope stack, - unuseds, urls, extraModules = [], @@ -17536,22 +18912,28 @@ var JSHINT = (function() { } var meta = token.meta; - if (meta && meta.isFutureReservedWord && state.inES5()) { - // ES3 FutureReservedWord in an ES5 environment. - if (!meta.es5) { + if (meta && meta.isFutureReservedWord) { + if (meta.moduleOnly && !state.option.module) { return false; } - // Some ES5 FutureReservedWord identifiers are active only - // within a strict mode environment. - if (meta.strictOnly) { - if (!state.option.strict && !state.isStrict()) { + if (state.inES5()) { + // ES3 FutureReservedWord in an ES5 environment. + if (!meta.es5) { return false; } - } - if (token.isProperty) { - return false; + // Some ES5 FutureReservedWord identifiers are active only + // within a strict mode environment. + if (meta.strictOnly) { + if (!state.option.strict && !state.isStrict()) { + return false; + } + } + + if (token.isProperty) { + return false; + } } } @@ -17589,28 +18971,38 @@ var JSHINT = (function() { } function assume() { + var badESOpt = null; processenforceall(); - if (!state.option.es3) { + /** + * TODO: Remove in JSHint 3 + */ + badESOpt = state.inferEsVersion(); + if (badESOpt) { + quit("E059", state.tokens.next, "esversion", badESOpt); + } + + if (state.inES5()) { combine(predefined, vars.ecmaIdentifiers[5]); } - if (state.option.esnext) { + if (state.inES6()) { combine(predefined, vars.ecmaIdentifiers[6]); } + /** + * Use `in` to check for the presence of any explicitly-specified value for + * `globalstrict` because both `true` and `false` should trigger an error. + */ + if (state.option.strict === "global" && "globalstrict" in state.option) { + quit("E059", state.tokens.next, "strict", "globalstrict"); + } + if (state.option.module) { - /** - * TODO: Extend this restriction to *all* "environmental" options. - */ - if (!hasParsedCode(funct)) { - error("E055", state.tokens.next, "module"); - } - /** * TODO: Extend this restriction to *all* ES6-specific options. */ - if (!state.inESNext()) { + if (!state.inES6()) { warning("W134", state.tokens.next, "module", 6); } } @@ -17691,10 +19083,6 @@ var JSHINT = (function() { combine(predefined, vars.wsh); } - if (state.option.globalstrict && state.option.strict !== false) { - state.option.strict = true; - } - if (state.option.yui) { combine(predefined, vars.yui); } @@ -17705,24 +19093,25 @@ var JSHINT = (function() { } // Produce an error warning. - function quit(code, line, chr) { - var percentage = Math.floor((line / state.lines.length) * 100); + function quit(code, token, a, b) { + var percentage = Math.floor((token.line / state.lines.length) * 100); var message = messages.errors[code].desc; - throw { + var exception = { name: "JSHintError", - line: line, - character: chr, + line: token.line, + character: token.from, message: message + " (" + percentage + "% scanned).", raw: message, - code: code + code: code, + a: a, + b: b }; - } - function isundef(scope, code, token, a) { - if (!state.ignored[code] && state.option.undef !== false) { - JSHINT.undefs.push([scope, code, token, a]); - } + exception.reason = supplant(message, exception) + " (" + percentage + + "% scanned)."; + + throw exception; } function removeIgnoredMessages() { @@ -17751,8 +19140,8 @@ var JSHINT = (function() { t = state.tokens.curr; } - l = t.line || 0; - ch = t.from || 0; + l = t.line; + ch = t.from; w = { id: "(error)", @@ -17774,7 +19163,7 @@ var JSHINT = (function() { removeIgnoredMessages(); if (JSHINT.errors.length >= state.option.maxerr) - quit("E043", l, ch); + quit("E043", t); return w; } @@ -17809,88 +19198,26 @@ var JSHINT = (function() { return i; } - // adds an indentifier to the relevant current scope and creates warnings/errors as necessary - // name: string - // opts: { type: string, token: token, isblockscoped: bool } - function addlabel(name, opts) { - - var type = opts.type; - var token = opts.token; - var isblockscoped = opts.isblockscoped; - - // Define label in the current function in the current scope. - if (type === "exception") { - if (_.has(funct["(context)"], name)) { - if (funct[name] !== true && !state.option.node) { - warning("W002", state.tokens.next, name); - } - } - } - - if (_.has(funct, name) && !funct["(global)"]) { - if (funct[name] === true) { - if (state.option.latedef) { - if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || - !_.contains([funct[name], type], "unction")) { - warning("W003", state.tokens.next, name); - } - } - } else { - if ((!state.option.shadow || _.contains([ "inner", "outer" ], state.option.shadow)) && - type !== "exception" || funct["(blockscope)"].getlabel(name)) { - warning("W004", state.tokens.next, name); - } - } - } - - if (funct["(context)"] && _.has(funct["(context)"], name) && type !== "function") { - if (state.option.shadow === "outer") { - warning("W123", state.tokens.next, name); - } - } - - // if the identifier is blockscoped (a let or a const), add it only to the current blockscope - if (isblockscoped) { - funct["(blockscope)"].current.add(name, type, state.tokens.curr); - if (funct["(blockscope)"].atTop() && exported[name]) { - state.tokens.curr.exported = true; - } - } else { - funct["(blockscope)"].shadow(name); - funct[name] = type; - - if (token) { - funct["(tokens)"][name] = token; - } - - if (funct["(global)"]) { - global[name] = funct; - if (_.has(implied, name)) { - if (state.option.latedef) { - if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || - !_.contains([funct[name], type], "unction")) { - warning("W003", state.tokens.next, name); - } - } - - delete implied[name]; - } - } else { - scope[name] = funct; - } - } - } - function doOption() { var nt = state.tokens.next; var body = nt.body.split(",").map(function(s) { return s.trim(); }); + var predef = {}; if (nt.type === "globals") { - body.forEach(function(g) { + body.forEach(function(g, idx) { g = g.split(":"); var key = (g[0] || "").trim(); var val = (g[1] || "").trim(); + if (key === "-" || !key.length) { + // Ignore trailing comma + if (idx > 0 && idx === body.length - 1) { + return; + } + error("E002", nt); + return; + } + if (key.charAt(0) === "-") { key = key.slice(1); val = false; @@ -17912,8 +19239,17 @@ var JSHINT = (function() { } if (nt.type === "exported") { - body.forEach(function(e) { - exported[e] = true; + body.forEach(function(e, idx) { + if (!e.length) { + // Ignore trailing comma + if (idx > 0 && idx === body.length - 1) { + return; + } + error("E002", nt); + return; + } + + state.funct["(scope)"].addExported(e); }); } @@ -17972,16 +19308,10 @@ var JSHINT = (function() { return; } - if (key === "es5") { - if (val === "true" && state.option.es5) { - warning("I003"); - } - } - if (key === "validthis") { // `validthis` is valid only within a function scope. - if (funct["(global)"]) + if (state.funct["(global)"]) return void error("E009"); if (val !== "true" && val !== "false") @@ -18062,12 +19392,6 @@ var JSHINT = (function() { if (key === "ignore") { switch (val) { - case "start": - state.ignoreLinterErrors = true; - break; - case "end": - state.ignoreLinterErrors = false; - break; case "line": state.ignoredLines[nt.line] = true; removeIgnoredMessages(); @@ -18078,6 +19402,54 @@ var JSHINT = (function() { return; } + if (key === "strict") { + switch (val) { + case "true": + state.option.strict = true; + break; + case "false": + state.option.strict = false; + break; + case "global": + case "implied": + state.option.strict = val; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "module") { + /** + * TODO: Extend this restriction to *all* "environmental" options. + */ + if (!hasParsedCode(state.funct)) { + error("E055", state.tokens.next, "module"); + } + } + + if (key === "esversion") { + switch (val) { + case "3": + case "5": + case "6": + state.option.moz = false; + state.option.esversion = +val; + break; + case "2015": + state.option.moz = false; + state.option.esversion = 6; + break; + default: + error("E002", nt); + } + if (!hasParsedCode(state.funct)) { + error("E055", state.tokens.next, "esversion"); + } + return; + } + var match = /^([+-])(W\d{3})$/g.exec(key); if (match) { // ignore for -W..., unignore for +W... @@ -18098,9 +19470,6 @@ var JSHINT = (function() { state.option[key] = (val === "true"); } - if (key === "newcap") { - state.option["(explicitNewcap)"] = true; - } return; } @@ -18118,7 +19487,11 @@ var JSHINT = (function() { // for ( var i = ... function peek(p) { - var i = p || 0, j = 0, t; + var i = p || 0, j = lookahead.length, t; + + if (i < j) { + return lookahead[i]; + } while (j <= i) { t = lookahead[j]; @@ -18185,7 +19558,7 @@ var JSHINT = (function() { state.tokens.next = lookahead.shift() || lex.token(); if (!state.tokens.next) { // No more tokens left, give up - quit("E041", state.tokens.curr.line); + quit("E041", state.tokens.curr); } if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { @@ -18197,7 +19570,11 @@ var JSHINT = (function() { } if (state.tokens.next.isSpecial) { - doOption(); + if (state.tokens.next.type === "falls through") { + state.tokens.curr.caseFallsThrough = true; + } else { + doOption(); + } } else { if (state.tokens.next.id !== "(endline)") { break; @@ -18252,7 +19629,7 @@ var JSHINT = (function() { } isLetExpr = true; // create a new block scope we use only for the current expression - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); advance("let"); advance("("); state.tokens.prev.fud(); @@ -18274,7 +19651,7 @@ var JSHINT = (function() { advance(); if (initial) { - funct["(verb)"] = state.tokens.curr.value; + state.funct["(verb)"] = state.tokens.curr.value; state.tokens.curr.beginsStmt = true; } @@ -18287,9 +19664,7 @@ var JSHINT = (function() { error("E030", state.tokens.curr, state.tokens.curr.id); } - // TODO: use pratt mechanics rather than special casing template tokens - while ((rbp < state.tokens.next.lbp || state.tokens.next.type === "(template)") && - !isEndOfExpr()) { + while (rbp < state.tokens.next.lbp && !isEndOfExpr()) { isArray = state.tokens.curr.value === "Array"; isObject = state.tokens.curr.value === "Object"; @@ -18329,7 +19704,7 @@ var JSHINT = (function() { } } if (isLetExpr) { - funct["(blockscope)"].unstack(); + state.funct["(scope)"].unstack(); } state.nameStack.pop(); @@ -18362,16 +19737,16 @@ var JSHINT = (function() { function nobreakcomma(left, right) { if (left.line !== startLine(right)) { if (!state.option.laxcomma) { - if (comma.first) { + if (parseComma.first) { warning("I001"); - comma.first = false; + parseComma.first = false; } warning("W014", left, right.value); } } } - function comma(opts) { + function parseComma(opts) { opts = opts || {}; if (!opts.peek) { @@ -18485,12 +19860,12 @@ var JSHINT = (function() { warning("W017", this); } + if (this.right && this.right.isMetaProperty) { + error("E031", this); // detect increment/decrement of a const // in the case of a.b, right will be the "." punctuator - if (this.right && this.right.identifier) { - if (funct["(blockscope)"].labeltype(this.right.value) === "const") { - error("E013", this, this.right.value); - } + } else if (this.right && this.right.identifier) { + state.funct["(scope)"].block.modify(this.right.value, this); } } @@ -18561,7 +19936,6 @@ var JSHINT = (function() { return x; } - function application(s) { var x = symbol(s, 42); @@ -18590,7 +19964,7 @@ var JSHINT = (function() { } if (!left || !right) { - quit("E041", state.tokens.curr.line); + quit("E041", state.tokens.curr); } if (left.id === "!") { @@ -18647,7 +20021,7 @@ var JSHINT = (function() { if (!left || !right) return false; - values = state.inESNext() ? typeofValues.es6 : typeofValues.es3; + values = state.inES6() ? typeofValues.es6 : typeofValues.es3; if (right.type === "(identifier)" && right.value === "typeof" && left.type === "(string)") return !_.contains(values, left.value); @@ -18655,11 +20029,11 @@ var JSHINT = (function() { return false; } - function isGlobalEval(left, state, funct) { + function isGlobalEval(left, state) { var isGlobal = false; // permit methods to refer to an "eval" key in their own context - if (left.type === "this" && funct["(context)"] === null) { + if (left.type === "this" && state.funct["(context)"] === null) { isGlobal = true; } // permit use of "eval" members of objects @@ -18696,78 +20070,99 @@ var JSHINT = (function() { while (!obj.identifier && typeof obj.left === "object") obj = obj.left; - if (obj.identifier && natives.indexOf(obj.value) >= 0) + if (obj.identifier && natives.indexOf(obj.value) >= 0 && + state.funct["(scope)"].isPredefined(obj.value)) { return obj.value; + } } var prototype = walkPrototype(left); if (prototype) return walkNative(prototype); } + /** + * Checks the left hand side of an assignment for issues, returns if ok + * @param {token} left - the left hand side of the assignment + * @param {token=} assignToken - the token for the assignment, used for reporting + * @param {object=} options - optional object + * @param {boolean} options.allowDestructuring - whether to allow destructuting binding + * @returns {boolean} Whether the left hand side is OK + */ + function checkLeftSideAssign(left, assignToken, options) { + + var allowDestructuring = options && options.allowDestructuring; + + assignToken = assignToken || left; + + if (state.option.freeze) { + var nativeObject = findNativePrototype(left); + if (nativeObject) + warning("W121", left, nativeObject); + } + if (checkPunctuator(left, "...")) { + left = left.right; + } + + if (left.identifier && !left.isMetaProperty) { + // reassign also calls modify + // but we are specific in order to catch function re-assignment + // and globals re-assignment + state.funct["(scope)"].block.reassign(left.value, left); + } + + if (left.id === ".") { + if (!left.left || left.left.value === "arguments" && !state.isStrict()) { + warning("E031", assignToken); + } + + state.nameStack.set(state.tokens.prev); + return true; + } else if (left.id === "{" || left.id === "[") { + if (allowDestructuring && left.destructAssign) { + left.destructAssign.forEach(function(t) { + if (t.id) { + state.funct["(scope)"].block.modify(t.id, t.token); + } + }); + } else { + if (left.id === "{" || !left.left) { + warning("E031", assignToken); + } else if (left.left.value === "arguments" && !state.isStrict()) { + warning("E031", assignToken); + } + } + + if (left.id === "[") { + state.nameStack.set(left.right); + } + + return true; + } else if (left.identifier && !isReserved(left) && !left.isMetaProperty) { + if (state.funct["(scope)"].labeltype(left.value) === "exception") { + warning("W022", left); + } + state.nameStack.set(left); + return true; + } + + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); + } else { + error("E031", assignToken); + } + + return false; + } + function assignop(s, f, p) { var x = infix(s, typeof f === "function" ? f : function(left, that) { that.left = left; - if (left) { - if (state.option.freeze) { - var nativeObject = findNativePrototype(left); - if (nativeObject) - warning("W121", left, nativeObject); - } + checkLeftSideAssign(left, that, { allowDestructuring: true }); - if (predefined[left.value] === false && - scope[left.value]["(global)"] === true) { - warning("W020", left); - } else if (left["function"]) { - warning("W021", left, left.value); - } + that.right = expression(10); - if (funct["(blockscope)"].labeltype(left.value) === "const") { - error("E013", left, left.value); - } - - if (left.id === ".") { - if (!left.left) { - warning("E031", that); - } else if (left.left.value === "arguments" && !state.isStrict()) { - warning("E031", that); - } - - state.nameStack.set(state.tokens.prev); - that.right = expression(10); - return that; - } else if (left.id === "[") { - if (state.tokens.curr.left.first) { - state.tokens.curr.left.first.forEach(function(t) { - if (t && funct[t.value] === "const") { - error("E013", t, t.value); - } - }); - } else if (!left.left) { - warning("E031", that); - } else if (left.left.value === "arguments" && !state.isStrict()) { - warning("E031", that); - } - - state.nameStack.set(left.right); - - that.right = expression(10); - return that; - } else if (left.identifier && !isReserved(left)) { - if (funct[left.value] === "exception") { - warning("W022", left); - } - state.nameStack.set(left); - that.right = expression(10); - return that; - } - - if (left === state.syntax["function"]) { - warning("W023", state.tokens.curr); - } - } - - error("E031", that); + return that; }, p); x.exps = true; @@ -18790,25 +20185,17 @@ var JSHINT = (function() { return x; } - function bitwiseassignop(s) { return assignop(s, function(left, that) { if (state.option.bitwise) { warning("W016", that, that.id); } - if (left) { - if (left.id === "." || left.id === "[" || - (left.identifier && !isReserved(left))) { - expression(10); - return that; - } - if (left === state.syntax["function"]) { - warning("W023", state.tokens.curr); - } - return that; - } - error("E031", that); + checkLeftSideAssign(left, that); + + that.right = expression(10); + + return that; }, 20); } @@ -18824,12 +20211,12 @@ var JSHINT = (function() { warning("W017", this); } + if (left.isMetaProperty) { + error("E031", this); // detect increment/decrement of a const // in the case of a.b, left will be the "." punctuator - if (left && left.identifier) { - if (funct["(blockscope)"].labeltype(left.value) === "const") { - error("E013", this, left.value); - } + } else if (left && left.identifier) { + state.funct["(scope)"].block.modify(left.value, left); } this.left = left; @@ -18883,14 +20270,14 @@ var JSHINT = (function() { // parameter destructuring with rest operator if (state.tokens.next.value === "...") { - if (!state.option.esnext) { - warning("W119", state.tokens.next, "spread/rest operator"); + if (!state.inES6(true)) { + warning("W119", state.tokens.next, "spread/rest operator", "6"); } advance(); - if (checkPunctuators(state.tokens.next, ["..."])) { + if (checkPunctuator(state.tokens.next, "...")) { warning("E024", state.tokens.next, "..."); - while (checkPunctuators(state.tokens.next, ["..."])) { + while (checkPunctuator(state.tokens.next, "...")) { advance(); } } @@ -18947,12 +20334,18 @@ var JSHINT = (function() { if (state.tokens.next.id !== ";") { // don't complain about unclosed templates / strings if (state.tokens.next.isUnclosed) return advance(); - if (!state.option.asi) { + + var sameLine = startLine(state.tokens.next) === state.tokens.curr.line && + state.tokens.next.id !== "(end)"; + var blockEnd = checkPunctuator(state.tokens.next, "}"); + + if (sameLine && !blockEnd) { + errorAt("E058", state.tokens.curr.line, state.tokens.curr.character); + } else if (!state.option.asi) { // If this is the last statement in a block that ends on // the same line *and* option lastsemic is on, ignore the warning. // Otherwise, complain about missing semicolon. - if (!state.option.lastsemic || state.tokens.next.id !== "}" || - startLine(state.tokens.next) !== state.tokens.curr.line) { + if ((blockEnd && !state.option.lastsemic) || !sameLine) { warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); } } @@ -18962,7 +20355,7 @@ var JSHINT = (function() { } function statement() { - var i = indent, r, s = scope, t = state.tokens.next; + var i = indent, r, t = state.tokens.next, hasOwnScope = false; if (t.id === ";") { advance(";"); @@ -18981,28 +20374,13 @@ var JSHINT = (function() { res = false; } - // detect a module import declaration - if (t.value === "module" && t.type === "(identifier)") { - if (peek().type === "(identifier)") { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "module"); - } - - advance("module"); - var name = identifier(); - addlabel(name, { type: "unused", token: state.tokens.curr }); - advance("from"); - advance("(string)"); - parseFinalSemicolon(); - return; - } - } - if (t.identifier && !res && peek().id === ":") { advance(); advance(":"); - scope = Object.create(s); - addlabel(t.value, { type: "label" }); + + hasOwnScope = true; + state.funct["(scope)"].stack(); + state.funct["(scope)"].block.addBreakLabel(t.value, { token: state.tokens.curr }); if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { warning("W028", state.tokens.next, t.value, state.tokens.next.value); @@ -19022,7 +20400,7 @@ var JSHINT = (function() { // ... // } // } - var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":"); + var iscase = (state.funct["(verb)"] === "case" && state.tokens.curr.value === ":"); block(true, true, false, false, iscase); return; } @@ -19031,10 +20409,10 @@ var JSHINT = (function() { r = expression(0, true); - if (r && (!r.identifier || r.value !== "function") && (r.type !== "(punctuator)")) { - if (!state.isStrict() && - state.option.globalstrict && - state.option.strict) { + if (r && !(r.identifier && r.value === "function") && + !(r.type === "(punctuator)" && r.left && + r.left.identifier && r.left.value === "function")) { + if (!state.isStrict() && state.stmtMissingStrict()) { warning("E007"); } } @@ -19054,7 +20432,9 @@ var JSHINT = (function() { // Restore the indentation. indent = i; - scope = s; + if (hasOwnScope) { + state.funct["(scope)"].unstack(); + } return r; } @@ -19098,36 +20478,35 @@ var JSHINT = (function() { p = pn; } else if (pn.value === "[" || pn.value === ".") { // string -> [ | . is a valid production - return; + break; } else if (!state.option.asi || pn.value === "(") { // string -> ( is not a valid production warning("W033", state.tokens.next); } } else if (p.id === "." || p.id === "[") { - return; + break; } else if (p.id !== ";") { warning("W033", p); } advance(); - if (state.directive[state.tokens.curr.value]) { - warning("W034", state.tokens.curr, state.tokens.curr.value); - } - - if (state.tokens.curr.value === "use strict") { - if (!state.option["(explicitNewcap)"]) { - state.option.newcap = true; - } - state.option.undef = true; + var directive = state.tokens.curr.value; + if (state.directive[directive] || + (directive === "use strict" && state.option.strict === "implied")) { + warning("W034", state.tokens.curr, directive); } // there's no directive negation, so always set to true - state.directive[state.tokens.curr.value] = true; + state.directive[directive] = true; if (p.id === ";") { advance(";"); } } + + if (state.isStrict()) { + state.option.undef = true; + } } @@ -19146,19 +20525,15 @@ var JSHINT = (function() { b = inblock, old_indent = indent, m, - s = scope, t, line, d; inblock = ordinary; - if (!ordinary || !state.option.funcscope) - scope = Object.create(scope); - t = state.tokens.next; - var metrics = funct["(metrics)"]; + var metrics = state.funct["(metrics)"]; metrics.nestedBlockDepth += 1; metrics.verifyMaxNestedBlockDepthPerFunction(); @@ -19166,7 +20541,8 @@ var JSHINT = (function() { advance("{"); // create a new block scope - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); + state.funct["(noblockscopedvar)"] = false; line = state.tokens.curr.line; if (state.tokens.next.id !== "}") { @@ -19184,7 +20560,7 @@ var JSHINT = (function() { } directives(); - if (state.option.strict && funct["(context)"]["(global)"]) { + if (state.option.strict && state.funct["(context)"]["(global)"]) { if (!m["use strict"] && !state.isStrict()) { warning("E007"); } @@ -19195,20 +20571,25 @@ var JSHINT = (function() { metrics.statementCount += a.length; - if (isfunc) { - state.directive = m; - } - indent -= state.option.indent; } advance("}", t); - funct["(blockscope)"].unstack(); + if (isfunc) { + state.funct["(scope)"].validateParams(); + if (m) { + state.directive = m; + } + } + + state.funct["(scope)"].unstack(); indent = old_indent; } else if (!ordinary) { if (isfunc) { + state.funct["(scope)"].stack(); + m = {}; if (stmt && !isfatarrow && !state.inMoz()) { error("W118", state.tokens.curr, "function closure expressions"); @@ -19223,18 +20604,22 @@ var JSHINT = (function() { } expression(10); - if (state.option.strict && funct["(context)"]["(global)"]) { + if (state.option.strict && state.funct["(context)"]["(global)"]) { if (!m["use strict"] && !state.isStrict()) { warning("E007"); } } + + state.funct["(scope)"].unstack(); } else { error("E021", state.tokens.next, "{", state.tokens.next.value); } } else { // check to avoid let declaration not within a block - funct["(noblockscopedvar)"] = true; + // though is fine inside for loop initializer section + state.funct["(noblockscopedvar)"] = state.tokens.next.id !== "for"; + state.funct["(scope)"].stack(); if (!stmt || state.option.curly) { warning("W116", state.tokens.next, "{", state.tokens.next.value); @@ -19246,11 +20631,12 @@ var JSHINT = (function() { a = [statement()]; indent -= state.option.indent; - delete funct["(noblockscopedvar)"]; + state.funct["(scope)"].unstack(); + delete state.funct["(noblockscopedvar)"]; } // Don't clear and let it propagate out if it is "break", "return" or similar in switch case - switch (funct["(verb)"]) { + switch (state.funct["(verb)"]) { case "break": case "continue": case "return": @@ -19261,10 +20647,9 @@ var JSHINT = (function() { /* falls through */ default: - funct["(verb)"] = null; + state.funct["(verb)"] = null; } - if (!ordinary || !state.option.funcscope) scope = s; inblock = b; if (ordinary && state.option.noempty && (!a || a.length === 0)) { warning("W035", state.tokens.prev); @@ -19285,18 +20670,6 @@ var JSHINT = (function() { } } - - function note_implied(tkn) { - var name = tkn.value; - var desc = Object.getOwnPropertyDescriptor(implied, name); - - if (!desc) - implied[name] = [tkn.line]; - else - desc.value.push(tkn.line); - } - - // Build the syntax table by declaring the syntactic elements of the language. type("(number)", function() { @@ -19314,11 +20687,6 @@ var JSHINT = (function() { nud: function() { var v = this.value; - // s will be either the function object 'funct' that the identifier points at - // or it will be a boolean if it is a predefined variable - var s = scope[v]; - var f; - var block; // If this identifier is the lone parameter to a shorthand "fat arrow" // function definition, i.e. @@ -19332,107 +20700,8 @@ var JSHINT = (function() { return this; } - block = funct["(blockscope)"].getlabel(v); - - if (typeof s === "function") { - // Protection against accidental inheritance. - s = undefined; - } else if (!block && typeof s === "boolean") { - f = funct; - funct = functions[0]; - addlabel(v, { type: "var" }); - s = funct; - funct = f; - } - - // The name is in scope and defined in the current function or it exists in the blockscope. - if (funct === s || block) { - // Change 'unused' to 'var', and reject labels. - // the name is in a block scope. - switch (block ? block[v]["(type)"] : funct[v]) { - case "unused": - if (block) block[v]["(type)"] = "var"; - else funct[v] = "var"; - break; - case "unction": - if (block) block[v]["(type)"] = "function"; - else funct[v] = "function"; - this["function"] = true; - break; - case "const": - // consts can only exist inside block - // because they are never added to the scope, s - block[v]["(unused)"] = false; - break; - case "function": - this["function"] = true; - break; - case "label": - warning("W037", state.tokens.curr, v); - break; - } - } else { - // If the name is already defined in the current - // function, but not as outer, then there is a scope error. - - switch (funct[v]) { - case "closure": - case "function": - case "var": - case "unused": - warning("W038", state.tokens.curr, v); - break; - case "label": - warning("W037", state.tokens.curr, v); - break; - case "outer": - case "global": - break; - default: - // If the name is defined in an outer function, make an outer entry, - // and if it was unused, make it var. - if (s === true) { - funct[v] = true; - } else if (s === null) { - warning("W039", state.tokens.curr, v); - note_implied(state.tokens.curr); - } else if (typeof s !== "object") { - // if we're in a list comprehension, variables are declared - // locally and used before being defined. So we check - // the presence of the given variable in the comp array - // before declaring it undefined. - - if (!funct["(comparray)"].check(v)) { - isundef(funct, "W117", state.tokens.curr, v); - } - - // Explicitly mark the variable as used within function scopes - if (!funct["(global)"]) { - funct[v] = true; - } - - note_implied(state.tokens.curr); - } else { - switch (s[v]) { - case "function": - case "unction": - this["function"] = true; - s[v] = "closure"; - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "var": - case "unused": - s[v] = "closure"; - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "closure": - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "label": - warning("W037", state.tokens.curr, v); - } - } - } + if (!state.funct["(comparray)"].check(v)) { + state.funct["(scope)"].block.use(v, state.tokens.curr); } return this; }, @@ -19443,11 +20712,11 @@ var JSHINT = (function() { }; var baseTemplateSyntax = { - lbp: 0, identifier: false, template: true, }; state.syntax["(template)"] = _.extend({ + lbp: 155, type: "(template)", nud: doTemplateLiteral, led: doTemplateLiteral, @@ -19455,18 +20724,21 @@ var JSHINT = (function() { }, baseTemplateSyntax); state.syntax["(template middle)"] = _.extend({ + lbp: 0, type: "(template middle)", middle: true, noSubst: false }, baseTemplateSyntax); state.syntax["(template tail)"] = _.extend({ + lbp: 0, type: "(template tail)", tail: true, noSubst: false }, baseTemplateSyntax); state.syntax["(no subst template)"] = _.extend({ + lbp: 155, type: "(template)", nud: doTemplateLiteral, led: doTemplateLiteral, @@ -19481,7 +20753,9 @@ var JSHINT = (function() { // ECMAScript parser delim("(endline)"); - delim("(begin)"); + (function(x) { + x.line = x.from = 0; + })(delim("(begin)")); delim("(end)").reach = true; delim("(error)").reach = true; delim("}").reach = true; @@ -19499,7 +20773,7 @@ var JSHINT = (function() { reserve("default").reach = true; reserve("finally"); reservevar("arguments", function(x) { - if (state.isStrict() && funct["(global)"]) { + if (state.isStrict() && state.funct["(global)"]) { warning("E008", x); } }); @@ -19509,8 +20783,8 @@ var JSHINT = (function() { reservevar("null"); reservevar("this", function(x) { if (state.isStrict() && !isMethod() && - !state.option.validthis && ((funct["(statement)"] && - funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { + !state.option.validthis && ((state.funct["(statement)"] && + state.funct["(name)"].charAt(0) > "Z") || state.funct["(global)"])) { warning("W040", x); } }); @@ -19540,7 +20814,7 @@ var JSHINT = (function() { warning("W127"); } - if (!comma({ peek: true })) { + if (!parseComma({ peek: true })) { return that; } while (true) { @@ -19548,7 +20822,7 @@ var JSHINT = (function() { break; } that.exprs.push(expr); - if (state.tokens.next.value !== "," || !comma()) { + if (state.tokens.next.value !== "," || !parseComma()) { break; } } @@ -19576,7 +20850,8 @@ var JSHINT = (function() { bitwise("^", "bitxor", 80); bitwise("&", "bitand", 90); relation("==", function(left, right) { - var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); + var eqnull = state.option.eqnull && + ((left && left.value) === "null" || (right && right.value) === "null"); switch (true) { case !eqnull && state.option.eqeqeq: @@ -19609,7 +20884,7 @@ var JSHINT = (function() { }); relation("!=", function(left, right) { var eqnull = state.option.eqnull && - (left.value === "null" || right.value === "null"); + ((left && left.value) === "null" || (right && right.value) === "null"); if (!eqnull && state.option.eqeqeq) { this.from = this.character; @@ -19720,13 +20995,13 @@ var JSHINT = (function() { warning("W016", this, "~"); } this.arity = "unary"; - expression(150); + this.right = expression(150); return this; }); prefix("...", function() { - if (!state.option.esnext) { - warning("W119", this, "spread/rest operator"); + if (!state.inES6(true)) { + warning("W119", this, "spread/rest operator", "6"); } // TODO: Allow all AssignmentExpression @@ -19766,7 +21041,7 @@ var JSHINT = (function() { error("E030", state.tokens.next, state.tokens.next.value); } - expression(150); + this.right = expression(150); return this; }); @@ -19775,7 +21050,7 @@ var JSHINT = (function() { this.right = expression(150); if (!this.right) { // '!' followed by nothing? Give up. - quit("E041", this.line || 0); + quit("E041", this); } if (bang[this.right.id] === true) { @@ -19786,7 +21061,11 @@ var JSHINT = (function() { prefix("typeof", (function() { var p = expression(150); - this.first = p; + this.first = this.right = p; + + if (!p) { // 'typeof' followed by nothing? Give up. + quit("E041", this); + } // The `typeof` operator accepts unresolvable references, so the operand // may be undefined. @@ -19796,6 +21075,22 @@ var JSHINT = (function() { return this; })); prefix("new", function() { + var mp = metaProperty("target", function() { + if (!state.inES6(true)) { + warning("W119", state.tokens.prev, "new.target", "6"); + } + var inFunction, c = state.funct; + while (c) { + inFunction = !c["(global)"]; + if (!c["(arrow)"]) { break; } + c = c["(context)"]; + } + if (!inFunction) { + warning("W136", state.tokens.prev, "new.target"); + } + }); + if (mp) { return mp; } + var c = expression(155), i; if (c && c.id !== "function") { if (c.identifier) { @@ -19809,7 +21104,7 @@ var JSHINT = (function() { warning("W053", state.tokens.prev, c.value); break; case "Symbol": - if (state.option.esnext) { + if (state.inES6()) { warning("W053", state.tokens.prev, c.value); } break; @@ -19825,7 +21120,8 @@ var JSHINT = (function() { default: if (c.id !== "function") { i = c.value.substr(0, 1); - if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { + if (state.option.newcap && (i < "A" || i > "Z") && + !state.funct["(scope)"].isPredefined(c.value)) { warning("W055", state.tokens.curr); } } @@ -19842,7 +21138,7 @@ var JSHINT = (function() { if (state.tokens.next.id !== "(" && !state.option.supernew) { warning("W058", state.tokens.curr, state.tokens.curr.value); } - this.first = c; + this.first = this.right = c; return this; }); state.syntax["new"].exps = true; @@ -19874,7 +21170,7 @@ var JSHINT = (function() { } if (!state.option.evil && (m === "eval" || m === "execScript")) { - if (isGlobalEval(left, state, funct)) { + if (isGlobalEval(left, state)) { warning("W061"); } } @@ -19893,7 +21189,7 @@ var JSHINT = (function() { if (left) { if (left.type === "(identifier)") { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { - if ("Number String Boolean Date Object Error Symbol".indexOf(left.value) === -1) { + if ("Array Number String Boolean Date Object Error Symbol".indexOf(left.value) === -1) { if (left.value === "Math") { warning("W063", left); } else if (state.option.newcap) { @@ -19911,14 +21207,14 @@ var JSHINT = (function() { if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } } advance(")"); if (typeof left === "object") { - if (state.inES3() && left.value === "parseInt" && n === 1) { + if (!state.inES5() && left.value === "parseInt" && n === 1) { warning("W065", state.tokens.curr); } if (!state.option.evil) { @@ -19945,9 +21241,9 @@ var JSHINT = (function() { addInternalSrc(left, p[0].value); } } - if (!left.identifier && left.id !== "." && left.id !== "[" && - left.id !== "(" && left.id !== "&&" && left.id !== "||" && - left.id !== "?" && !(state.option.esnext && left["(name)"])) { + if (!left.identifier && left.id !== "." && left.id !== "[" && left.id !== "=>" && + left.id !== "(" && left.id !== "&&" && left.id !== "||" && left.id !== "?" && + !(state.inES6() && left["(name)"])) { warning("W067", that); } } @@ -20001,7 +21297,7 @@ var JSHINT = (function() { warning("W127"); } - comma(); + parseComma(); } } @@ -20046,7 +21342,13 @@ var JSHINT = (function() { // operator. (isFunctor(ret) && !isEndOfExpr()) || // Used as the return value of a single-statement arrow function - (ret.id === "{" && preceeding.id === "=>"); + (ret.id === "{" && preceeding.id === "=>") || + // Used to delineate an integer number literal from a dereferencing + // punctuator (otherwise interpreted as a decimal point) + (ret.type === "(number)" && + checkPunctuator(pn, ".") && /^\d+$/.test(ret.value)) || + // Used to wrap object destructuring assignment + (opening.beginsStmt && ret.id === "=" && ret.left.id === "{"); } } @@ -20054,7 +21356,7 @@ var JSHINT = (function() { // The operator may be necessary to override the default binding power of // neighboring operators (whenever there is an operator in use within the // first expression *or* the current group contains multiple expressions) - if (!isNecessary && (first.left || ret.exprs)) { + if (!isNecessary && (first.left || first.right || ret.exprs)) { isNecessary = (!isBeginOfExpr(preceeding) && first.lbp <= preceeding.lbp) || (!isEndOfExpr() && last.lbp < state.tokens.next.lbp); @@ -20076,7 +21378,7 @@ var JSHINT = (function() { var e = expression(10), s; if (e && e.type === "(string)") { if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { - if (isGlobalEval(left, state, funct)) { + if (isGlobalEval(left, state)) { warning("W061"); } } @@ -20103,7 +21405,7 @@ var JSHINT = (function() { function comprehensiveArrayExpression() { var res = {}; res.exps = true; - funct["(comparray)"].stack(); + state.funct["(comparray)"].stack(); // Handle reversed for expressions, used in spidermonkey var reversed = false; @@ -20112,7 +21414,7 @@ var JSHINT = (function() { if (!state.inMoz()) { warning("W116", state.tokens.next, "for", state.tokens.next.value); } - funct["(comparray)"].setState("use"); + state.funct["(comparray)"].setState("use"); res.right = expression(10); } @@ -20124,44 +21426,45 @@ var JSHINT = (function() { } } advance("("); - funct["(comparray)"].setState("define"); + state.funct["(comparray)"].setState("define"); res.left = expression(130); if (_.contains(["in", "of"], state.tokens.next.value)) { advance(); } else { error("E045", state.tokens.curr); } - funct["(comparray)"].setState("generate"); + state.funct["(comparray)"].setState("generate"); expression(10); advance(")"); if (state.tokens.next.value === "if") { advance("if"); advance("("); - funct["(comparray)"].setState("filter"); + state.funct["(comparray)"].setState("filter"); res.filter = expression(10); advance(")"); } if (!reversed) { - funct["(comparray)"].setState("use"); + state.funct["(comparray)"].setState("use"); res.right = expression(10); } advance("]"); - funct["(comparray)"].unstack(); + state.funct["(comparray)"].unstack(); return res; } prefix("[", function() { var blocktype = lookupBlockType(); if (blocktype.isCompArray) { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "array comprehension"); + if (!state.option.esnext && !state.inMoz()) { + warning("W118", state.tokens.curr, "array comprehension"); } return comprehensiveArrayExpression(); - } else if (blocktype.isDestAssign && !state.inESNext()) { - warning("W104", state.tokens.curr, "destructuring assignment"); + } else if (blocktype.isDestAssign) { + this.destructAssign = destructuringPattern({ openingParsed: true, assignment: true }); + return this; } var b = state.tokens.curr.line !== startLine(state.tokens.next); this.first = []; @@ -20195,8 +21498,8 @@ var JSHINT = (function() { this.first.push(expression(10)); if (state.tokens.next.id === ",") { - comma({ allowTrailing: true }); - if (state.tokens.next.id === "]" && !state.inES5(true)) { + parseComma({ allowTrailing: true }); + if (state.tokens.next.id === "]" && !state.inES5()) { warning("W070", state.tokens.curr); break; } @@ -20213,8 +21516,8 @@ var JSHINT = (function() { function isMethod() { - return funct["(statement)"] && funct["(statement)"].type === "class" || - funct["(context)"] && funct["(context)"]["(verb)"] === "class"; + return state.funct["(statement)"] && state.funct["(statement)"].type === "class" || + state.funct["(context)"] && state.funct["(context)"]["(verb)"] === "class"; } @@ -20264,20 +21567,22 @@ var JSHINT = (function() { * single-argument shorthand. * @param {bool} [options.parsedOpening] Whether the opening parenthesis has * already been parsed. + * @returns {{ arity: number, params: Array.}} */ function functionparams(options) { var next; - var params = []; + var paramsIds = []; var ident; var tokens = []; var t; var pastDefault = false; var pastRest = false; + var arity = 0; var loneArg = options && options.loneArg; if (loneArg && loneArg.identifier === true) { - addlabel(loneArg.value, { type: "unused", token: loneArg }); - return [loneArg.value]; + state.funct["(scope)"].addParam(loneArg.value, loneArg); + return { arity: 1, params: [ loneArg.value ] }; } next = state.tokens.next; @@ -20291,60 +21596,73 @@ var JSHINT = (function() { return; } + function addParam(addParamArgs) { + state.funct["(scope)"].addParam.apply(state.funct["(scope)"], addParamArgs); + } + for (;;) { + arity++; + // are added to the param scope + var currentParams = []; + if (_.contains(["{", "["], state.tokens.next.id)) { - tokens = destructuringExpression(); + tokens = destructuringPattern(); for (t in tokens) { t = tokens[t]; if (t.id) { - params.push(t.id); - addlabel(t.id, { type: "unused", token: t.token }); + paramsIds.push(t.id); + currentParams.push([t.id, t.token]); } } } else { - if (checkPunctuators(state.tokens.next, ["..."])) pastRest = true; + if (checkPunctuator(state.tokens.next, "...")) pastRest = true; ident = identifier(true); if (ident) { - params.push(ident); - addlabel(ident, { type: "unused", token: state.tokens.curr }); + paramsIds.push(ident); + currentParams.push([ident, state.tokens.curr]); } else { // Skip invalid parameter. while (!checkPunctuators(state.tokens.next, [",", ")"])) advance(); } } - // it is a syntax error to have a regular argument after a default argument + // It is valid to have a regular argument after a default argument + // since undefined can be used for missing parameters. Still warn as it is + // a possible code smell. if (pastDefault) { if (state.tokens.next.id !== "=") { - error("E051", state.tokens.current); + error("W138", state.tokens.current); } } if (state.tokens.next.id === "=") { - if (!state.inESNext()) { - warning("W119", state.tokens.next, "default parameters"); + if (!state.inES6()) { + warning("W119", state.tokens.next, "default parameters", "6"); } advance("="); pastDefault = true; expression(10); } + + // now we have evaluated the default expression, add the variable to the param scope + currentParams.forEach(addParam); + if (state.tokens.next.id === ",") { if (pastRest) { warning("W131", state.tokens.next); } - comma(); + parseComma(); } else { advance(")", next); - return params; + return { arity: arity, params: paramsIds }; } } } - function functor(name, token, scope, overwrites) { + function functor(name, token, overwrites) { var funct = { "(name)" : name, "(breakage)" : 0, "(loopage)" : 0, - "(scope)" : scope, "(tokens)" : {}, "(properties)": {}, @@ -20356,9 +21674,10 @@ var JSHINT = (function() { "(metrics)" : null, "(statement)" : null, "(context)" : null, - "(blockscope)": null, + "(scope)" : null, "(comparray)" : null, "(generator)" : null, + "(arrow)" : null, "(params)" : null }; @@ -20373,7 +21692,7 @@ var JSHINT = (function() { _.extend(funct, overwrites); if (funct["(context)"]) { - funct["(blockscope)"] = funct["(context)"]["(blockscope)"]; + funct["(scope)"] = funct["(context)"]["(scope)"]; funct["(comparray)"] = funct["(context)"]["(comparray)"]; } @@ -20448,10 +21767,9 @@ var JSHINT = (function() { * the body of member functions. */ function doFunction(options) { - var f, name, statement, classExprBinding, isGenerator, isArrow; + var f, token, name, statement, classExprBinding, isGenerator, isArrow, ignoreLoopFunc; var oldOption = state.option; var oldIgnored = state.ignored; - var oldScope = scope; if (options) { name = options.name; @@ -20459,37 +21777,53 @@ var JSHINT = (function() { classExprBinding = options.classExprBinding; isGenerator = options.type === "generator"; isArrow = options.type === "arrow"; + ignoreLoopFunc = options.ignoreLoopFunc; } state.option = Object.create(state.option); state.ignored = Object.create(state.ignored); - scope = Object.create(scope); - funct = functor(name || state.nameStack.infer(), state.tokens.next, scope, { + state.funct = functor(name || state.nameStack.infer(), state.tokens.next, { "(statement)": statement, - "(context)": funct, + "(context)": state.funct, + "(arrow)": isArrow, "(generator)": isGenerator }); - f = funct; - state.tokens.curr.funct = funct; + f = state.funct; + token = state.tokens.curr; + token.funct = state.funct; - functions.push(funct); + functions.push(state.funct); - if (name) { - addlabel(name, { type: "function" }); + // So that the function is available to itself and referencing itself is not + // seen as a closure, add the function name to a new scope, but do not + // test for unused (unused: false) + // it is a new block scope so that params can override it, it can be block scoped + // but declarations inside the function don't cause already declared error + state.funct["(scope)"].stack("functionouter"); + var internallyAccessibleName = name || classExprBinding; + if (internallyAccessibleName) { + state.funct["(scope)"].block.add(internallyAccessibleName, + classExprBinding ? "class" : "function", state.tokens.curr, false); } - if (classExprBinding) { - addlabel(classExprBinding, { type: "function" }); - } + // create the param scope (params added in functionparams) + state.funct["(scope)"].stack("functionparams"); - funct["(params)"] = functionparams(options); - funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); + var paramsInfo = functionparams(options); + + if (paramsInfo) { + state.funct["(params)"] = paramsInfo.params; + state.funct["(metrics)"].arity = paramsInfo.arity; + state.funct["(metrics)"].verifyMaxParametersPerFunction(); + } else { + state.funct["(metrics)"].arity = 0; + } if (isArrow) { - if (!state.option.esnext) { - warning("W119", state.tokens.curr, "arrow function syntax (=>)"); + if (!state.inES6(true)) { + warning("W119", state.tokens.curr, "arrow function syntax (=>)", "6"); } if (!options.loneArg) { @@ -20500,26 +21834,34 @@ var JSHINT = (function() { block(false, true, true, isArrow); if (!state.option.noyield && isGenerator && - funct["(generator)"] !== "yielded") { + state.funct["(generator)"] !== "yielded") { warning("W124", state.tokens.curr); } - funct["(metrics)"].verifyMaxStatementsPerFunction(); - funct["(metrics)"].verifyMaxComplexityPerFunction(); - funct["(unusedOption)"] = state.option.unused; - - scope = oldScope; + state.funct["(metrics)"].verifyMaxStatementsPerFunction(); + state.funct["(metrics)"].verifyMaxComplexityPerFunction(); + state.funct["(unusedOption)"] = state.option.unused; state.option = oldOption; state.ignored = oldIgnored; - funct["(last)"] = state.tokens.curr.line; - funct["(lastcharacter)"] = state.tokens.curr.character; + state.funct["(last)"] = state.tokens.curr.line; + state.funct["(lastcharacter)"] = state.tokens.curr.character; - _.map(Object.keys(funct), function(key) { - if (key[0] === "(") return; - funct["(blockscope)"].unshadow(key); - }); + // unstack the params scope + state.funct["(scope)"].unstack(); // also does usage and label checks - funct = funct["(context)"]; + // unstack the function outer stack + state.funct["(scope)"].unstack(); + + state.funct = state.funct["(context)"]; + + if (!ignoreLoopFunc && !state.option.loopfunc && state.funct["(loopage)"]) { + // If the function we just parsed accesses any non-local variables + // trigger a warning. Otherwise, the function is safe even within + // a loop. + if (f["(isCapturing)"]) { + warning("W083", token); + } + } return f; } @@ -20529,6 +21871,7 @@ var JSHINT = (function() { statementCount: 0, nestedBlockDepth: -1, ComplexityCount: 1, + arity: 0, verifyMaxStatementsPerFunction: function() { if (state.option.maxstatements && @@ -20537,11 +21880,10 @@ var JSHINT = (function() { } }, - verifyMaxParametersPerFunction: function(params) { - params = params || []; - - if (_.isNumber(state.option.maxparams) && params.length > state.option.maxparams) { - warning("W072", functionStartToken, params.length); + verifyMaxParametersPerFunction: function() { + if (_.isNumber(state.option.maxparams) && + this.arity > state.option.maxparams) { + warning("W072", functionStartToken, this.arity); } }, @@ -20564,7 +21906,7 @@ var JSHINT = (function() { } function increaseComplexityCount() { - funct["(metrics)"].ComplexityCount += 1; + state.funct["(metrics)"].ComplexityCount += 1; } // Parse assignments that were found instead of conditionals. @@ -20604,17 +21946,32 @@ var JSHINT = (function() { // Check for lonely setters if in the ES5 mode. if (state.inES5()) { for (var name in props) { - if (_.has(props, name) && props[name].setterToken && !props[name].getterToken) { + if (props[name] && props[name].setterToken && !props[name].getterToken) { warning("W078", props[name].setterToken); } } } } + function metaProperty(name, c) { + if (checkPunctuator(state.tokens.next, ".")) { + var left = state.tokens.curr.id; + advance("."); + var id = identifier(); + state.tokens.curr.isMetaProperty = true; + if (name !== id) { + error("E057", state.tokens.prev, left, id); + } else { + c(); + } + return state.tokens.curr; + } + } + (function(x) { x.nud = function() { var b, f, i, p, t, isGeneratorMethod = false, nextVal; - var props = {}; // All properties, including accessors + var props = Object.create(null); // All properties, including accessors b = state.tokens.curr.line !== startLine(state.tokens.next); if (b) { @@ -20624,6 +21981,12 @@ var JSHINT = (function() { } } + var blocktype = lookupBlockType(); + if (blocktype.isDestAssign) { + this.destructAssign = destructuringPattern({ openingParsed: true, assignment: true }); + return this; + } + for (;;) { if (state.tokens.next.id === "}") { break; @@ -20632,8 +21995,8 @@ var JSHINT = (function() { nextVal = state.tokens.next.value; if (state.tokens.next.identifier && (peekIgnoreEOL().id === "," || peekIgnoreEOL().id === "}")) { - if (!state.inESNext()) { - warning("W104", state.tokens.next, "object short notation"); + if (!state.inES6()) { + warning("W104", state.tokens.next, "object short notation", "6"); } i = propertyName(true); saveProperty(props, i, state.tokens.next); @@ -20651,8 +22014,8 @@ var JSHINT = (function() { // ES6 allows for get() {...} and set() {...} method // definition shorthand syntax, so we don't produce an error - // if the esnext option is enabled. - if (!i && !state.inESNext()) { + // if linting ECMAScript 6 code. + if (!i && !state.inES6()) { error("E035"); } @@ -20669,13 +22032,13 @@ var JSHINT = (function() { // Don't warn about getter/setter pairs if this is an ES6 concise method if (nextVal === "get" && i && p) { warning("W076", t, p[0], i); - } else if (nextVal === "set" && i && (!p || p.length !== 1)) { + } else if (nextVal === "set" && i && f["(metrics)"].arity !== 1) { warning("W077", t, i); } } else { if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { - if (!state.inESNext()) { - warning("W104", state.tokens.next, "generator functions"); + if (!state.inES6()) { + warning("W104", state.tokens.next, "generator functions", "6"); } advance("*"); isGeneratorMethod = true; @@ -20697,8 +22060,8 @@ var JSHINT = (function() { } if (state.tokens.next.value === "(") { - if (!state.inESNext()) { - warning("W104", state.tokens.curr, "concise methods"); + if (!state.inES6()) { + warning("W104", state.tokens.curr, "concise methods", "6"); } doFunction({ type: isGeneratorMethod ? "generator" : null }); } else { @@ -20710,10 +22073,10 @@ var JSHINT = (function() { countMember(i); if (state.tokens.next.id === ",") { - comma({ allowTrailing: true, property: true }); + parseComma({ allowTrailing: true, property: true }); if (state.tokens.next.id === ",") { warning("W070", state.tokens.curr); - } else if (state.tokens.next.id === "}" && !state.inES5(true)) { + } else if (state.tokens.next.id === "}" && !state.inES5()) { warning("W070", state.tokens.curr); } } else { @@ -20734,86 +22097,156 @@ var JSHINT = (function() { }; }(delim("{"))); - function destructuringExpression() { - var id, ids; - var identifiers = []; - if (!state.inESNext()) { - warning("W104", state.tokens.curr, "destructuring expression"); + function destructuringPattern(options) { + var isAssignment = options && options.assignment; + + if (!state.inES6()) { + warning("W104", state.tokens.curr, + isAssignment ? "destructuring assignment" : "destructuring binding", "6"); } + + return destructuringPatternRecursive(options); + } + + function destructuringPatternRecursive(options) { + var ids; + var identifiers = []; + var openingParsed = options && options.openingParsed; + var isAssignment = options && options.assignment; + var recursiveOptions = isAssignment ? { assignment: isAssignment } : null; + var firstToken = openingParsed ? state.tokens.curr : state.tokens.next; + var nextInnerDE = function() { var ident; if (checkPunctuators(state.tokens.next, ["[", "{"])) { - ids = destructuringExpression(); + ids = destructuringPatternRecursive(recursiveOptions); for (var id in ids) { id = ids[id]; identifiers.push({ id: id.id, token: id.token }); } - } else if (checkPunctuators(state.tokens.next, [","])) { + } else if (checkPunctuator(state.tokens.next, ",")) { identifiers.push({ id: null, token: state.tokens.curr }); - } else if (checkPunctuators(state.tokens.next, ["("])) { + } else if (checkPunctuator(state.tokens.next, "(")) { advance("("); nextInnerDE(); advance(")"); } else { - var is_rest = checkPunctuators(state.tokens.next, ["..."]); - ident = identifier(); - if (ident) + var is_rest = checkPunctuator(state.tokens.next, "..."); + + if (isAssignment) { + var assignTarget = expression(20); + if (assignTarget) { + checkLeftSideAssign(assignTarget); + + // if the target was a simple identifier, add it to the list to return + if (assignTarget.identifier) { + ident = assignTarget.value; + } + } + } else { + ident = identifier(); + } + if (ident) { identifiers.push({ id: ident, token: state.tokens.curr }); + } return is_rest; } return false; }; - if (checkPunctuators(state.tokens.next, ["["])) { - advance("["); - var element_after_rest = false; - if (nextInnerDE() && checkPunctuators(state.tokens.next, [","]) && - !element_after_rest) { - warning("W130", state.tokens.next); - element_after_rest = true; - } - while (!checkPunctuators(state.tokens.next, ["]"])) { - advance(","); - if (checkPunctuators(state.tokens.next, ["]"])) { - // Trailing comma - break; - } - if (nextInnerDE() && checkPunctuators(state.tokens.next, [","]) && - !element_after_rest) { - warning("W130", state.tokens.next); - element_after_rest = true; - } - } - advance("]"); - } else if (checkPunctuators(state.tokens.next, ["{"])) { - advance("{"); - id = identifier(); - if (checkPunctuators(state.tokens.next, [":"])) { + var assignmentProperty = function() { + var id; + if (checkPunctuator(state.tokens.next, "[")) { + advance("["); + expression(10); + advance("]"); + advance(":"); + nextInnerDE(); + } else if (state.tokens.next.id === "(string)" || + state.tokens.next.id === "(number)") { + advance(); advance(":"); nextInnerDE(); } else { - identifiers.push({ id: id, token: state.tokens.curr }); - } - while (!checkPunctuators(state.tokens.next, ["}"])) { - advance(","); - if (checkPunctuators(state.tokens.next, ["}"])) { - // Trailing comma - // ObjectBindingPattern: { BindingPropertyList , } - break; - } + // this id will either be the property name or the property name and the assigning identifier id = identifier(); - if (checkPunctuators(state.tokens.next, [":"])) { + if (checkPunctuator(state.tokens.next, ":")) { advance(":"); nextInnerDE(); - } else { + } else if (id) { + // in this case we are assigning (not declaring), so check assignment + if (isAssignment) { + checkLeftSideAssign(state.tokens.curr); + } identifiers.push({ id: id, token: state.tokens.curr }); } } + }; + + var id, value; + if (checkPunctuator(firstToken, "[")) { + if (!openingParsed) { + advance("["); + } + if (checkPunctuator(state.tokens.next, "]")) { + warning("W137", state.tokens.curr); + } + var element_after_rest = false; + while (!checkPunctuator(state.tokens.next, "]")) { + if (nextInnerDE() && !element_after_rest && + checkPunctuator(state.tokens.next, ",")) { + warning("W130", state.tokens.next); + element_after_rest = true; + } + if (checkPunctuator(state.tokens.next, "=")) { + if (checkPunctuator(state.tokens.prev, "...")) { + advance("]"); + } else { + advance("="); + } + id = state.tokens.prev; + value = expression(10); + if (value && value.type === "undefined") { + warning("W080", id, id.value); + } + } + if (!checkPunctuator(state.tokens.next, "]")) { + advance(","); + } + } + advance("]"); + } else if (checkPunctuator(firstToken, "{")) { + + if (!openingParsed) { + advance("{"); + } + if (checkPunctuator(state.tokens.next, "}")) { + warning("W137", state.tokens.curr); + } + while (!checkPunctuator(state.tokens.next, "}")) { + assignmentProperty(); + if (checkPunctuator(state.tokens.next, "=")) { + advance("="); + id = state.tokens.prev; + value = expression(10); + if (value && value.type === "undefined") { + warning("W080", id, id.value); + } + } + if (!checkPunctuator(state.tokens.next, "}")) { + advance(","); + if (checkPunctuator(state.tokens.next, "}")) { + // Trailing comma + // ObjectBindingPattern: { BindingPropertyList , } + break; + } + } + } advance("}"); } return identifiers; } - function destructuringExpressionMatch(tokens, value) { + function destructuringPatternMatch(tokens, value) { var first = value.first; if (!first) @@ -20839,8 +22272,8 @@ var JSHINT = (function() { var isConst = type === "const"; var tokens, lone, value, letblock; - if (!state.inESNext()) { - warning("W104", state.tokens.curr, type); + if (!state.inES6()) { + warning("W104", state.tokens.curr, type, "6"); } if (isLet && state.tokens.next.value === "(") { @@ -20848,9 +22281,9 @@ var JSHINT = (function() { warning("W118", state.tokens.next, "let block"); } advance("("); - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); letblock = true; - } else if (funct["(noblockscopedvar)"]) { + } else if (state.funct["(noblockscopedvar)"]) { error("E048", state.tokens.curr, isConst ? "Const" : "Let"); } @@ -20858,73 +22291,68 @@ var JSHINT = (function() { for (;;) { var names = []; if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); + tokens = destructuringPattern(); lone = false; } else { tokens = [ { id: identifier(), token: state.tokens.curr } ]; lone = true; - if (inexport) { - exported[state.tokens.curr.value] = true; - state.tokens.curr.exported = true; - } } - for (var t in tokens) { - if (tokens.hasOwnProperty(t)) { - t = tokens[t]; - if (state.inESNext()) { - // only look in the latest scope because we can shadow - if (funct["(blockscope)"].current.labeltype(t.id) === "const") { - warning("E011", null, t.id); - } - } - if (funct["(global)"]) { - if (predefined[t.id] === false) { - warning("W079", t.token, t.id); - } - } - if (t.id && !funct["(noblockscopedvar)"]) { - addlabel(t.id, { - type: isConst ? "const" : "unused", - token: t.token, - isblockscoped: true }); - names.push(t.token); - } - } - } - - statement.first = statement.first.concat(names); if (!prefix && isConst && state.tokens.next.id !== "=") { warning("E012", state.tokens.curr, state.tokens.curr.value); } + for (var t in tokens) { + if (tokens.hasOwnProperty(t)) { + t = tokens[t]; + if (state.funct["(scope)"].block.isGlobal()) { + if (predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + } + if (t.id && !state.funct["(noblockscopedvar)"]) { + state.funct["(scope)"].addlabel(t.id, { + type: type, + token: t.token }); + names.push(t.token); + + if (lone && inexport) { + state.funct["(scope)"].setExported(t.token.value, t.token); + } + } + } + } + if (state.tokens.next.id === "=") { advance("="); - if (!prefix && state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } if (!prefix && peek(0).id === "=" && state.tokens.next.identifier) { warning("W120", state.tokens.next, state.tokens.next.value); } + var id = state.tokens.prev; // don't accept `in` in expression if prefix is used for ForIn/Of loop. value = expression(prefix ? 120 : 10); + if (!prefix && value && value.type === "undefined") { + warning("W080", id, id.value); + } if (lone) { tokens[0].first = value; } else { - destructuringExpressionMatch(names, value); + destructuringPatternMatch(names, value); } } + statement.first = statement.first.concat(names); + if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } if (letblock) { advance(")"); block(true, true); statement.block = true; - funct["(blockscope)"].unstack(); + state.funct["(scope)"].unstack(); } return statement; @@ -20953,90 +22381,81 @@ var JSHINT = (function() { for (;;) { var names = []; if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); + tokens = destructuringPattern(); lone = false; } else { tokens = [ { id: identifier(), token: state.tokens.curr } ]; lone = true; - if (inexport) { - exported[state.tokens.curr.value] = true; - state.tokens.curr.exported = true; - } } + + if (!(prefix && implied) && report && state.option.varstmt) { + warning("W132", this); + } + + this.first = this.first.concat(names); + for (var t in tokens) { if (tokens.hasOwnProperty(t)) { t = tokens[t]; - if (state.inESNext()) { - // because var is function scoped, look in the whole function - if (funct["(blockscope)"].labeltype(t.id) === "const") { - warning("E011", null, t.id); - } - } - if (!implied && funct["(global)"]) { + if (!implied && state.funct["(global)"] && !state.impliedClosure()) { if (predefined[t.id] === false) { warning("W079", t.token, t.id); } else if (state.option.futurehostile === false) { if ((!state.inES5() && vars.ecmaIdentifiers[5][t.id] === false) || - (!state.inESNext() && vars.ecmaIdentifiers[6][t.id] === false)) { + (!state.inES6() && vars.ecmaIdentifiers[6][t.id] === false)) { warning("W129", t.token, t.id); } } } if (t.id) { if (implied === "for") { - var ident = t.token.value; - switch (funct[ident]) { - case "unused": - funct[ident] = "var"; - break; - case "var": - break; - default: - if (!funct["(blockscope)"].getlabel(ident) && - !(scope[ident] || {})[ident]) { - if (report) warning("W088", t.token, ident); - } + + if (!state.funct["(scope)"].has(t.id)) { + if (report) warning("W088", t.token, t.id); } + state.funct["(scope)"].block.use(t.id, t.token); } else { - addlabel(t.id, { type: "unused", token: t.token }); + state.funct["(scope)"].addlabel(t.id, { + type: "var", + token: t.token }); + + if (lone && inexport) { + state.funct["(scope)"].setExported(t.id, t.token); + } } names.push(t.token); } } } - if (!prefix && report && state.option.varstmt) { - warning("W132", this); - } - - this.first = this.first.concat(names); - if (state.tokens.next.id === "=") { state.nameStack.set(state.tokens.curr); advance("="); - if (!prefix && report && state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } if (peek(0).id === "=" && state.tokens.next.identifier) { if (!prefix && report && - !funct["(params)"] || funct["(params)"].indexOf(state.tokens.next.value) === -1) { + !state.funct["(params)"] || + state.funct["(params)"].indexOf(state.tokens.next.value) === -1) { warning("W120", state.tokens.next, state.tokens.next.value); } } + var id = state.tokens.prev; // don't accept `in` in expression if prefix is used for ForIn/Of loop. value = expression(prefix ? 120 : 10); + if (value && !prefix && report && !state.funct["(loopage)"] && value.type === "undefined") { + warning("W080", id, id.value); + } if (lone) { tokens[0].first = value; } else { - destructuringExpressionMatch(names, value); + destructuringPatternMatch(names, value); } } if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } return this; @@ -21050,13 +22469,16 @@ var JSHINT = (function() { function classdef(isStatement) { /*jshint validthis:true */ - if (!state.inESNext()) { - warning("W104", state.tokens.curr, "class"); + if (!state.inES6()) { + warning("W104", state.tokens.curr, "class", "6"); } if (isStatement) { // BindingIdentifier this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); + + state.funct["(scope)"].addlabel(this.name, { + type: "class", + token: state.tokens.curr }); } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { // BindingIdentifier(opt) this.name = identifier(); @@ -21089,8 +22511,8 @@ var JSHINT = (function() { var isStatic; var isGenerator; var getset; - var props = {}; - var staticProps = {}; + var props = Object.create(null); + var staticProps = Object.create(null); var computed; for (var i = 0; state.tokens.next.id !== "}"; ++i) { name = state.tokens.next; @@ -21120,7 +22542,7 @@ var JSHINT = (function() { advance(); computed = false; if (name.identifier && name.value === "static") { - if (checkPunctuators(state.tokens.next, ["*"])) { + if (checkPunctuator(state.tokens.next, "*")) { isGenerator = true; advance("*"); } @@ -21150,11 +22572,11 @@ var JSHINT = (function() { continue; } - if (!checkPunctuators(state.tokens.next, ["("])) { + if (!checkPunctuator(state.tokens.next, "(")) { // error --- class properties must be methods error("E054", state.tokens.next, state.tokens.next.value); while (state.tokens.next.id !== "}" && - !checkPunctuators(state.tokens.next, ["("])) { + !checkPunctuator(state.tokens.next, "(")) { advance(); } if (state.tokens.next.value !== "(") { @@ -21196,37 +22618,37 @@ var JSHINT = (function() { checkProperties(props); } - blockstmt("function", function() { + blockstmt("function", function(context) { + var inexport = context && context.inexport; var generator = false; if (state.tokens.next.value === "*") { advance("*"); - if (state.inESNext({ strict: true })) { + if (state.inES6(true)) { generator = true; } else { - warning("W119", state.tokens.curr, "function*"); + warning("W119", state.tokens.curr, "function*", "6"); } } if (inblock) { warning("W082", state.tokens.curr); - } var i = optionalidentifier(); + state.funct["(scope)"].addlabel(i, { + type: "function", + token: state.tokens.curr }); + if (i === undefined) { warning("W025"); + } else if (inexport) { + state.funct["(scope)"].setExported(i, state.tokens.prev); } - // check if a identifier with the same name is already defined - // in the blockscope as a const - if (funct["(blockscope)"].labeltype(i) === "const") { - warning("E011", null, i); - } - addlabel(i, { type: "unction", token: state.tokens.curr }); - doFunction({ name: i, statement: this, - type: generator ? "generator" : null + type: generator ? "generator" : null, + ignoreLoopFunc: inblock // a declaration may already have warned }); if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { error("E039"); @@ -21238,27 +22660,15 @@ var JSHINT = (function() { var generator = false; if (state.tokens.next.value === "*") { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "function*"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "function*", "6"); } advance("*"); generator = true; } var i = optionalidentifier(); - var fn = doFunction({ name: i, type: generator ? "generator" : null }); - - function isVariable(name) { return name[0] !== "("; } - function isLocal(name) { return fn[name] === "var"; } - - if (!state.option.loopfunc && funct["(loopage)"]) { - // If the function we just parsed accesses any non-local variables - // trigger a warning. Otherwise, the function is safe even within - // a loop. - if (_.some(fn, function(val, name) { return isVariable(name) && !isLocal(name); })) { - warning("W083"); - } - } + doFunction({ name: i, type: generator ? "generator" : null }); return this; }); @@ -21290,7 +22700,7 @@ var JSHINT = (function() { // When the if is within a for-in loop and the condition has a negative form, // check if the body contains nothing but a continue statement if (forinifcheck && forinifcheck.type === "(negative)") { - if (s && s.length === 1 && s[0].type === "(identifier)" && s[0].value === "continue") { + if (s && s[0] && s[0].type === "(identifier)" && s[0].value === "continue") { forinifcheck.type = "(negative-with-continue)"; } } @@ -21310,32 +22720,23 @@ var JSHINT = (function() { var b; function doCatch() { - var oldScope = scope; - var e; - advance("catch"); advance("("); - scope = Object.create(oldScope); + state.funct["(scope)"].stack("catchparams"); - e = state.tokens.next.value; - if (state.tokens.next.type !== "(identifier)") { - e = null; - warning("E030", state.tokens.next, e); - } - - advance(); - - funct = functor("(catch)", state.tokens.next, scope, { - "(context)" : funct, - "(breakage)" : funct["(breakage)"], - "(loopage)" : funct["(loopage)"], - "(statement)": false, - "(catch)" : true - }); - - if (e) { - addlabel(e, { type: "exception" }); + if (checkPunctuators(state.tokens.next, ["[", "{"])) { + var tokens = destructuringPattern(); + _.each(tokens, function(token) { + if (token.id) { + state.funct["(scope)"].addParam(token.id, token, "exception"); + } + }); + } else if (state.tokens.next.type !== "(identifier)") { + warning("E030", state.tokens.next, state.tokens.next.value); + } else { + // only advance if we have an identifier so we can continue parsing in the most common error - that no param is given. + state.funct["(scope)"].addParam(identifier(), state.tokens.curr, "exception"); } if (state.tokens.next.value === "if") { @@ -21348,16 +22749,9 @@ var JSHINT = (function() { advance(")"); - state.tokens.curr.funct = funct; - functions.push(funct); - block(false); - scope = oldScope; - - funct["(last)"] = state.tokens.curr.line; - funct["(lastcharacter)"] = state.tokens.curr.character; - funct = funct["(context)"]; + state.funct["(scope)"].unstack(); } block(true); @@ -21386,15 +22780,15 @@ var JSHINT = (function() { blockstmt("while", function() { var t = state.tokens.next; - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); checkCondAssignment(expression(0)); advance(")", t); block(true, true); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; return this; }).labelled = true; @@ -21419,7 +22813,7 @@ var JSHINT = (function() { var g = false; var noindent = false; - funct["(breakage)"] += 1; + state.funct["(breakage)"] += 1; advance("("); checkCondAssignment(expression(0)); advance(")", t); @@ -21437,7 +22831,7 @@ var JSHINT = (function() { for (;;) { switch (state.tokens.next.id) { case "case": - switch (funct["(verb)"]) { + switch (state.funct["(verb)"]) { case "yield": case "break": case "case": @@ -21450,7 +22844,7 @@ var JSHINT = (function() { // You can tell JSHint that you don't use break intentionally by // adding a comment /* falls through */ on a line just before // the next `case`. - if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + if (!state.tokens.curr.caseFallsThrough) { warning("W086", state.tokens.curr, "case"); } } @@ -21460,10 +22854,10 @@ var JSHINT = (function() { increaseComplexityCount(); g = true; advance(":"); - funct["(verb)"] = "case"; + state.funct["(verb)"] = "case"; break; case "default": - switch (funct["(verb)"]) { + switch (state.funct["(verb)"]) { case "yield": case "break": case "continue": @@ -21474,7 +22868,7 @@ var JSHINT = (function() { // Do not display a warning if 'default' is the first statement or if // there is a special /* falls through */ comment. if (this.cases.length) { - if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + if (!state.tokens.curr.caseFallsThrough) { warning("W086", state.tokens.curr, "default"); } } @@ -21489,8 +22883,8 @@ var JSHINT = (function() { indent -= state.option.indent; advance("}", t); - funct["(breakage)"] -= 1; - funct["(verb)"] = undefined; + state.funct["(breakage)"] -= 1; + state.funct["(verb)"] = undefined; return; case "(end)": error("E023", state.tokens.next, "}"); @@ -21523,6 +22917,7 @@ var JSHINT = (function() { indent -= state.option.indent; } } + return this; }).labelled = true; stmt("debugger", function() { @@ -21534,8 +22929,8 @@ var JSHINT = (function() { (function() { var x = stmt("do", function() { - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; increaseComplexityCount(); this.first = block(true, true); @@ -21544,8 +22939,8 @@ var JSHINT = (function() { advance("("); checkCondAssignment(expression(0)); advance(")", t); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; return this; }); x.labelled = true; @@ -21565,8 +22960,6 @@ var JSHINT = (function() { } } - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); @@ -21587,16 +22980,16 @@ var JSHINT = (function() { else if (checkPunctuators(nextop, ["}", "]"])) --level; if (level < 0) break; if (level === 0) { - if (!comma && checkPunctuators(nextop, [","])) comma = nextop; - else if (!initializer && checkPunctuators(nextop, ["="])) initializer = nextop; + if (!comma && checkPunctuator(nextop, ",")) comma = nextop; + else if (!initializer && checkPunctuator(nextop, "=")) initializer = nextop; } } while (level > 0 || !_.contains(inof, nextop.value) && nextop.value !== ";" && nextop.type !== "(end)"); // Is this a JSCS bug? This looks really weird. // if we're in a for (… in|of …) statement if (_.contains(inof, nextop.value)) { - if (!state.inESNext() && nextop.value === "of") { - error("W104", nextop, "for of"); + if (!state.inES6() && nextop.value === "of") { + warning("W104", nextop, "for of", "6"); } var ok = !(initializer || comma); @@ -21615,7 +23008,7 @@ var JSHINT = (function() { advance(state.tokens.next.id); // create a new block scope letscope = true; - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); state.tokens.curr.fud({ prefix: true }); } else { // Parse as a var statement, with implied bindings. Ignore errors if an error @@ -21640,6 +23033,9 @@ var JSHINT = (function() { }); } + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; + s = block(true, true); if (nextop.value === "in" && state.option.forin) { @@ -21660,8 +23056,8 @@ var JSHINT = (function() { state.forinifcheckneeded = false; } - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; } else { if (foreachtok) { error("E045", foreachtok); @@ -21674,7 +23070,7 @@ var JSHINT = (function() { advance("let"); // create a new block scope letscope = true; - funct["(blockscope)"].stack(); + state.funct["(scope)"].stack(); state.tokens.curr.fud(); } else { for (;;) { @@ -21682,12 +23078,16 @@ var JSHINT = (function() { if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } } } nolinebreak(state.tokens.curr); advance(";"); + + // start loopage after the first ; as the next two expressions are executed + // on every loop + state.funct["(loopage)"] += 1; if (state.tokens.next.id !== ";") { checkCondAssignment(expression(0)); } @@ -21702,18 +23102,19 @@ var JSHINT = (function() { if (state.tokens.next.id !== ",") { break; } - comma(); + parseComma(); } } advance(")", t); + state.funct["(breakage)"] += 1; block(true, true); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; } // unstack loop blockscope if (letscope) { - funct["(blockscope)"].unstack(); + state.funct["(scope)"].unstack(); } return this; }).labelled = true; @@ -21722,22 +23123,19 @@ var JSHINT = (function() { stmt("break", function() { var v = state.tokens.next.value; - if (funct["(breakage)"] === 0) - warning("W052", state.tokens.next, this.value); - if (!state.option.asi) nolinebreak(this); - if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { - if (state.tokens.curr.line === startLine(state.tokens.next)) { - if (funct[v] !== "label") { - warning("W090", state.tokens.next, v); - } else if (scope[v] !== funct) { - warning("W091", state.tokens.next, v); - } - this.first = state.tokens.next; - advance(); + if (state.tokens.next.id !== ";" && !state.tokens.next.reach && + state.tokens.curr.line === startLine(state.tokens.next)) { + if (!state.funct["(scope)"].funct.hasBreakLabel(v)) { + warning("W090", state.tokens.next, v); } + this.first = state.tokens.next; + advance(); + } else { + if (state.funct["(breakage)"] === 0) + warning("W052", state.tokens.next, this.value); } reachable(this); @@ -21749,7 +23147,9 @@ var JSHINT = (function() { stmt("continue", function() { var v = state.tokens.next.value; - if (funct["(breakage)"] === 0) + if (state.funct["(breakage)"] === 0) + warning("W052", state.tokens.next, this.value); + if (!state.funct["(loopage)"]) warning("W052", state.tokens.next, this.value); if (!state.option.asi) @@ -21757,16 +23157,12 @@ var JSHINT = (function() { if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { if (state.tokens.curr.line === startLine(state.tokens.next)) { - if (funct[v] !== "label") { + if (!state.funct["(scope)"].funct.hasBreakLabel(v)) { warning("W090", state.tokens.next, v); - } else if (scope[v] !== funct) { - warning("W091", state.tokens.next, v); } this.first = state.tokens.next; advance(); } - } else if (!funct["(loopage)"]) { - warning("W052", state.tokens.next, this.value); } reachable(this); @@ -21803,15 +23199,15 @@ var JSHINT = (function() { x.lbp = 25; }(prefix("yield", function() { var prev = state.tokens.prev; - if (state.inESNext(true) && !funct["(generator)"]) { + if (state.inES6(true) && !state.funct["(generator)"]) { // If it's a yield within a catch clause inside a generator then that's ok - if (!("(catch)" === funct["(name)"] && funct["(context)"]["(generator)"])) { + if (!("(catch)" === state.funct["(name)"] && state.funct["(context)"]["(generator)"])) { error("E046", state.tokens.curr, "yield"); } - } else if (!state.inESNext()) { - warning("W104", state.tokens.curr, "yield"); + } else if (!state.inES6()) { + warning("W104", state.tokens.curr, "yield", "6"); } - funct["(generator)"] = "yielded"; + state.funct["(generator)"] = "yielded"; var delegatingYield = false; if (state.tokens.next.value === "*") { @@ -21821,7 +23217,8 @@ var JSHINT = (function() { if (this.line === startLine(state.tokens.next) || !state.inMoz()) { if (delegatingYield || - (state.tokens.next.id !== ";" && !state.tokens.next.reach && state.tokens.next.nud)) { + (state.tokens.next.id !== ";" && !state.option.asi && + !state.tokens.next.reach && state.tokens.next.nud)) { nobreaknonadjacent(state.tokens.curr, state.tokens.next); this.first = expression(10); @@ -21853,8 +23250,8 @@ var JSHINT = (function() { }).exps = true; stmt("import", function() { - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "import"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "import", "6"); } if (state.tokens.next.type === "(string)") { @@ -21866,7 +23263,11 @@ var JSHINT = (function() { if (state.tokens.next.identifier) { // ImportClause :: ImportedDefaultBinding this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); + // Import bindings are immutable (see ES6 8.1.1.5.5) + state.funct["(scope)"].addlabel(this.name, { + type: "const", + token: state.tokens.curr }); + if (state.tokens.next.value === ",") { // ImportClause :: ImportedDefaultBinding , NameSpaceImport // ImportClause :: ImportedDefaultBinding , NamedImports @@ -21888,7 +23289,10 @@ var JSHINT = (function() { advance("as"); if (state.tokens.next.identifier) { this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); + // Import bindings are immutable (see ES6 8.1.1.5.5) + state.funct["(scope)"].addlabel(this.name, { + type: "const", + token: state.tokens.curr }); } } else { // ImportClause :: NamedImports @@ -21909,7 +23313,11 @@ var JSHINT = (function() { advance("as"); importName = identifier(); } - addlabel(importName, { type: "unused", token: state.tokens.curr }); + + // Import bindings are immutable (see ES6 8.1.1.5.5) + state.funct["(scope)"].addlabel(importName, { + type: "const", + token: state.tokens.curr }); if (state.tokens.next.value === ",") { advance(","); @@ -21934,12 +23342,12 @@ var JSHINT = (function() { var token; var identifier; - if (!state.inESNext()) { - warning("W119", state.tokens.curr, "export"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "export", "6"); ok = false; } - if (!funct["(global)"] || !funct["(blockscope)"].atTop()) { + if (!state.funct["(scope)"].block.isGlobal()) { error("E053", state.tokens.curr); ok = false; } @@ -21953,11 +23361,14 @@ var JSHINT = (function() { } if (state.tokens.next.type === "default") { - // ExportDeclaration :: export default HoistableDeclaration - // ExportDeclaration :: export default ClassDeclaration + // ExportDeclaration :: + // export default [lookahead  { function, class }] AssignmentExpression[In] ; + // export default HoistableDeclaration + // export default ClassDeclaration state.nameStack.set(state.tokens.next); advance("default"); - if (state.tokens.next.id === "function" || state.tokens.next.id === "class") { + var exportType = state.tokens.next.id; + if (exportType === "function" || exportType === "class") { this.block = true; } @@ -21965,15 +23376,15 @@ var JSHINT = (function() { expression(10); - if (state.tokens.next.id === "class") { - identifier = token.name; - } else { - identifier = token.value; - } + identifier = token.value; - addlabel(identifier, { - type: "function", token: token - }); + if (this.block) { + state.funct["(scope)"].addlabel(identifier, { + type: exportType, + token: token }); + + state.funct["(scope)"].setExported(identifier, token); + } return this; } @@ -21988,7 +23399,6 @@ var JSHINT = (function() { } advance(); - state.tokens.curr.exported = ok; exportedTokens.push(state.tokens.curr); if (state.tokens.next.value === "as") { @@ -22015,11 +23425,7 @@ var JSHINT = (function() { advance("(string)"); } else if (ok) { exportedTokens.forEach(function(token) { - if (!funct[token.value]) { - isundef(funct, "W117", token, token.value); - } - exported[token.value] = true; - funct["(blockscope)"].setExported(token.value); + state.funct["(scope)"].setExported(token.value, token); }); } return this; @@ -22041,16 +23447,14 @@ var JSHINT = (function() { // ExportDeclaration :: export Declaration this.block = true; advance("function"); - exported[state.tokens.next.value] = ok; - state.tokens.next.exported = true; - state.syntax["function"].fud(); + state.syntax["function"].fud({ inexport:true }); } else if (state.tokens.next.id === "class") { // ExportDeclaration :: export Declaration this.block = true; advance("class"); - exported[state.tokens.next.value] = ok; - state.tokens.next.exported = true; + var classNameToken = state.tokens.next; state.syntax["class"].fud(); + state.funct["(scope)"].setExported(classNameToken.value, classNameToken); } else { error("E024", state.tokens.next, state.tokens.next.value); } @@ -22061,6 +23465,7 @@ var JSHINT = (function() { // Future Reserved Words FutureReservedWord("abstract"); + FutureReservedWord("await", { es5: true, moduleOnly: true }); FutureReservedWord("boolean"); FutureReservedWord("byte"); FutureReservedWord("char"); @@ -22093,14 +23498,16 @@ var JSHINT = (function() { // expression is a comprehension array, destructuring assignment or a json value. var lookupBlockType = function() { - var pn, pn1; + var pn, pn1, prev; var i = -1; var bracketStack = 0; var ret = {}; - if (checkPunctuators(state.tokens.curr, ["[", "{"])) + if (checkPunctuators(state.tokens.curr, ["[", "{"])) { bracketStack += 1; + } do { - pn = (i === -1) ? state.tokens.next : peek(i); + prev = i === -1 ? state.tokens.curr : pn; + pn = i === -1 ? state.tokens.next : peek(i); pn1 = peek(i + 1); i = i + 1; if (checkPunctuators(pn, ["[", "{"])) { @@ -22108,12 +23515,13 @@ var JSHINT = (function() { } else if (checkPunctuators(pn, ["]", "}"])) { bracketStack -= 1; } - if (pn.identifier && pn.value === "for" && bracketStack === 1) { + if (bracketStack === 1 && pn.identifier && pn.value === "for" && + !checkPunctuator(prev, ".")) { ret.isCompArray = true; ret.notJson = true; break; } - if (checkPunctuators(pn, ["}", "]"]) && bracketStack === 0) { + if (bracketStack === 0 && checkPunctuators(pn, ["}", "]"])) { if (pn1.value === "=") { ret.isDestAssign = true; ret.notJson = true; @@ -22123,7 +23531,7 @@ var JSHINT = (function() { break; } } - if (pn.value === ";") { + if (checkPunctuator(pn, ";")) { ret.isBlock = true; ret.notJson = true; } @@ -22138,10 +23546,10 @@ var JSHINT = (function() { name = tkn.value; } - if (props[name] && _.has(props, name)) { + if (props[name] && name !== "__proto__") { warning("W075", state.tokens.next, msg, name); } else { - props[name] = {}; + props[name] = Object.create(null); } props[name].basic = true; @@ -22173,12 +23581,12 @@ var JSHINT = (function() { state.tokens.curr.accessorType = accessorType; state.nameStack.set(tkn); - if (props[name] && _.has(props, name)) { - if (props[name].basic || props[name][flagName]) { + if (props[name]) { + if ((props[name].basic || props[name][flagName]) && name !== "__proto__") { warning("W075", state.tokens.next, msg, name); } } else { - props[name] = {}; + props[name] = Object.create(null); } props[name][flagName] = tkn; @@ -22186,29 +23594,47 @@ var JSHINT = (function() { function computedPropertyName() { advance("["); - if (!state.option.esnext) { - warning("W119", state.tokens.curr, "computed property names"); + if (!state.inES6()) { + warning("W119", state.tokens.curr, "computed property names", "6"); } var value = expression(10); advance("]"); return value; } - // Test whether a given token is a punctuator matching one of the specified values + /** + * Test whether a given token is a punctuator matching one of the specified values + * @param {Token} token + * @param {Array.} values + * @returns {boolean} + */ function checkPunctuators(token, values) { - return token.type === "(punctuator)" && _.contains(values, token.value); + if (token.type === "(punctuator)") { + return _.contains(values, token.value); + } + return false; + } + + /** + * Test whether a given token is a punctuator matching the specified value + * @param {Token} token + * @param {string} value + * @returns {boolean} + */ + function checkPunctuator(token, value) { + return token.type === "(punctuator)" && token.value === value; } // Check whether this function has been reached for a destructuring assign with undeclared values function destructuringAssignOrJsonValue() { - // lookup for the assignment (esnext only) + // lookup for the assignment (ECMAScript 6 only) // if it has semicolons, it is a block, so go parse it as a block // or it's not a block, but there are assignments, check for undeclared variables var block = lookupBlockType(); if (block.notJson) { - if (!state.inESNext() && block.isDestAssign) { - warning("W104", state.tokens.curr, "destructuring assignment"); + if (!state.inES6() && block.isDestAssign) { + warning("W104", state.tokens.curr, "destructuring assignment", "6"); } statements(); // otherwise parse json value @@ -22266,7 +23692,7 @@ var JSHINT = (function() { if (v.unused) warning("W098", v.token, v.raw_text || v.value); if (v.undef) - isundef(v.funct, "W117", v.token, v.value); + state.funct["(scope)"].block.use(v.value, v.token); }); _carrays.splice(-1, 1); _current = _carrays[_carrays.length - 1]; @@ -22283,7 +23709,7 @@ var JSHINT = (function() { if (_current && _current.mode === "use") { if (use(v)) { _current.variables.push({ - funct: funct, + funct: state.funct, token: state.tokens.curr, value: v, undef: true, @@ -22296,7 +23722,7 @@ var JSHINT = (function() { // check if the variable has been used previously if (!declare(v)) { _current.variables.push({ - funct: funct, + funct: state.funct, token: state.tokens.curr, value: v, undef: false, @@ -22306,14 +23732,14 @@ var JSHINT = (function() { return true; // When we are in the "generate" state of the list comp, } else if (_current && _current.mode === "generate") { - isundef(funct, "W117", state.tokens.curr, v); + state.funct["(scope)"].block.use(v, state.tokens.curr); return true; // When we are in "filter" state, } else if (_current && _current.mode === "filter") { // we check whether current variable has been declared if (use(v)) { // if not we warn about it - isundef(funct, "W117", state.tokens.curr, v); + state.funct["(scope)"].block.use(v, state.tokens.curr); } return true; } @@ -22408,141 +23834,6 @@ var JSHINT = (function() { } } - var warnUnused = function(name, tkn, type, unused_opt) { - var line = tkn.line; - var chr = tkn.from; - var raw_name = tkn.raw_text || name; - - if (unused_opt === undefined) { - unused_opt = state.option.unused; - } - - if (unused_opt === true) { - unused_opt = "last-param"; - } - - var warnable_types = { - "vars": ["var"], - "last-param": ["var", "param"], - "strict": ["var", "param", "last-param"] - }; - - if (unused_opt) { - if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { - if (!tkn.exported) { - warningAt("W098", line, chr, raw_name); - } - } - } - - unuseds.push({ - name: name, - line: line, - character: chr - }); - }; - - var blockScope = function() { - var _current = {}; - var _variables = [_current]; - - function _checkBlockLabels() { - for (var t in _current) { - var label = _current[t], - labelType = label["(type)"]; - if (labelType === "unused" || (labelType === "const" && label["(unused)"])) { - if (state.option.unused) { - var tkn = _current[t]["(token)"]; - // Don't report exported labels as unused - if (tkn.exported) { - continue; - } - - warnUnused(t, tkn, "var"); - } - } - } - } - - function _getLabel(l) { - for (var i = _variables.length - 1 ; i >= 0; --i) { - if (_.has(_variables[i], l) && !_variables[i][l]["(shadowed)"]) { - return _variables[i]; - } - } - } - - return { - stack: function() { - _current = {}; - _variables.push(_current); - }, - - unstack: function() { - _checkBlockLabels(); - _variables.splice(_variables.length - 1, 1); - _current = _.last(_variables); - }, - - getlabel: _getLabel, - - labeltype: function(l) { - // returns a labels type or null if not present - var block = _getLabel(l); - if (block) { - return block[l]["(type)"]; - } - return null; - }, - - shadow: function(name) { - for (var i = _variables.length - 1; i >= 0; i--) { - if (_.has(_variables[i], name)) { - _variables[i][name]["(shadowed)"] = true; - } - } - }, - - unshadow: function(name) { - for (var i = _variables.length - 1; i >= 0; i--) { - if (_.has(_variables[i], name)) { - _variables[i][name]["(shadowed)"] = false; - } - } - }, - - atTop: function() { - return _variables.length === 1; - }, - - setExported: function(id) { - if (funct["(blockscope)"].atTop()) { - var item = _current[id]; - if (item && item["(token)"]) { - item["(token)"].exported = true; - } - } - }, - - current: { - labeltype: function(t) { - if (_current[t]) { - return _current[t]["(type)"]; - } - return null; - }, - - has: function(t) { - return _.has(_current, t); - }, - - add: function(t, type, tok) { - _current[t] = { "(type)" : type, "(token)": tok, "(shadowed)": false, "(unused)": true }; - } - } - }; - }; - var escapeRegex = function(str) { return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); }; @@ -22574,7 +23865,7 @@ var JSHINT = (function() { combine(predefined, g || {}); declared = Object.create(null); - exported = Object.create(null); + var exported = Object.create(null); // Variables that live outside the current file function each(obj, cb) { if (!obj) @@ -22615,14 +23906,6 @@ var JSHINT = (function() { } else { var optionKey = optionKeys[x]; newOptionObj[optionKey] = o[optionKey]; - if (optionKey === "es5") { - if (o[optionKey]) { - warning("I003"); - } - } - - if (optionKeys[x] === "newcap" && o[optionKey] === false) - newOptionObj["(explicitNewcap)"] = true; } } } @@ -22634,25 +23917,30 @@ var JSHINT = (function() { state.option.maxerr = state.option.maxerr || 50; indent = 1; - global = Object.create(predefined); - scope = global; - funct = functor("(global)", null, scope, { + var scopeManagerInst = scopeManager(state, predefined, exported, declared); + scopeManagerInst.on("warning", function(ev) { + warning.apply(null, [ ev.code, ev.token].concat(ev.data)); + }); + + scopeManagerInst.on("error", function(ev) { + error.apply(null, [ ev.code, ev.token ].concat(ev.data)); + }); + + state.funct = functor("(global)", null, { "(global)" : true, - "(blockscope)": blockScope(), + "(scope)" : scopeManagerInst, "(comparray)" : arrayComprehension(), "(metrics)" : createMetrics(state.tokens.next) }); - functions = [funct]; + functions = [state.funct]; urls = []; stack = null; member = {}; membersOnly = null; - implied = {}; inblock = false; lookahead = []; - unuseds = []; if (!isString(s) && !Array.isArray(s)) { errorAt("E004", 0); @@ -22727,7 +24015,7 @@ var JSHINT = (function() { }); lex.on("fatal", function(ev) { - quit("E041", ev.line, ev.from); + quit("E041", ev); }); lex.on("Identifier", function(ev) { @@ -22751,15 +24039,15 @@ var JSHINT = (function() { } } - assume(); - - // combine the passed globals after we've assumed all our options - combine(predefined, g || {}); - - //reset values - comma.first = true; - try { + assume(); + + // combine the passed globals after we've assumed all our options + combine(predefined, g || {}); + + //reset values + parseComma.first = true; + advance(); switch (state.tokens.next.id) { case "{": @@ -22769,12 +24057,9 @@ var JSHINT = (function() { default: directives(); - if (state.isStrict()) { - if (!state.option.globalstrict) { - if (!(state.option.module || state.option.node || state.option.phantom || - state.option.browserify)) { - warning("W097", state.tokens.prev); - } + if (state.directive["use strict"]) { + if (!state.allowsGlobalUsd()) { + warning("W097", state.tokens.prev); } } @@ -22782,126 +24067,10 @@ var JSHINT = (function() { } if (state.tokens.next.id !== "(end)") { - quit("E041", state.tokens.curr.line); + quit("E041", state.tokens.curr); } - funct["(blockscope)"].unstack(); - - var markDefined = function(name, context) { - do { - if (typeof context[name] === "string") { - // JSHINT marks unused variables as 'unused' and - // unused function declaration as 'unction'. This - // code changes such instances back 'var' and - // 'closure' so that the code in JSHINT.data() - // doesn't think they're unused. - - if (context[name] === "unused") - context[name] = "var"; - else if (context[name] === "unction") - context[name] = "closure"; - - return true; - } - - context = context["(context)"]; - } while (context); - - return false; - }; - - var clearImplied = function(name, line) { - if (!implied[name]) - return; - - var newImplied = []; - for (var i = 0; i < implied[name].length; i += 1) { - if (implied[name][i] !== line) - newImplied.push(implied[name][i]); - } - - if (newImplied.length === 0) - delete implied[name]; - else - implied[name] = newImplied; - }; - - var checkUnused = function(func, key) { - var type = func[key]; - var tkn = func["(tokens)"][key]; - - if (key.charAt(0) === "(") - return; - - if (type !== "unused" && type !== "unction") - return; - - // Params are checked separately from other variables. - if (func["(params)"] && func["(params)"].indexOf(key) !== -1) - return; - - // Variable is in global scope and defined as exported. - if (func["(global)"] && _.has(exported, key)) - return; - - warnUnused(key, tkn, "var"); - }; - - // Check queued 'x is not defined' instances to see if they're still undefined. - for (i = 0; i < JSHINT.undefs.length; i += 1) { - k = JSHINT.undefs[i].slice(0); - - if (markDefined(k[2].value, k[0]) || k[2].forgiveUndef) { - clearImplied(k[2].value, k[2].line); - } else if (state.option.undef) { - warning.apply(warning, k.slice(1)); - } - } - - functions.forEach(function(func) { - if (func["(unusedOption)"] === false) { - return; - } - - for (var key in func) { - if (_.has(func, key)) { - checkUnused(func, key); - } - } - - if (!func["(params)"]) - return; - - var params = func["(params)"].slice(); - var param = params.pop(); - var type, unused_opt; - - while (param) { - type = func[param]; - unused_opt = func["(unusedOption)"] || state.option.unused; - unused_opt = unused_opt === true ? "last-param" : unused_opt; - - // 'undefined' is a special case for (function(window, undefined) { ... })(); - // patterns. - - if (param === "undefined") - return; - - if (type === "unused" || type === "unction") { - warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); - } else if (unused_opt === "last-param") { - return; - } - - param = params.pop(); - } - }); - - for (var key in declared) { - if (_.has(declared, key) && !_.has(global, key) && !_.has(exported, key)) { - warnUnused(key, declared[key], "var"); - } - } + state.funct["(scope)"].unstack(); } catch (err) { if (err && err.name === "JSHintError") { @@ -22910,7 +24079,7 @@ var JSHINT = (function() { scope : "(main)", raw : err.raw, code : err.code, - reason : err.message, + reason : err.reason, line : err.line || nt.line, character : err.character || nt.from }, null); @@ -22948,8 +24117,6 @@ var JSHINT = (function() { options: state.option }; - var implieds = []; - var members = []; var fu, f, i, j, n, globals; if (itself.errors.length) { @@ -22960,24 +24127,16 @@ var JSHINT = (function() { data.json = true; } - for (n in implied) { - if (_.has(implied, n)) { - implieds.push({ - name: n, - line: implied[n] - }); - } - } - - if (implieds.length > 0) { - data.implieds = implieds; + var impliedGlobals = state.funct["(scope)"].getImpliedGlobals(); + if (impliedGlobals.length > 0) { + data.implieds = impliedGlobals; } if (urls.length > 0) { data.urls = urls; } - globals = Object.keys(scope); + globals = state.funct["(scope)"].getUsedOrDefinedGlobals(); if (globals.length > 0) { data.globals = globals; } @@ -23005,18 +24164,18 @@ var JSHINT = (function() { fu.metrics = { complexity: f["(metrics)"].ComplexityCount, - parameters: (f["(params)"] || []).length, + parameters: f["(metrics)"].arity, statements: f["(metrics)"].statementCount }; data.functions.push(fu); } + var unuseds = state.funct["(scope)"].getUnuseds(); if (unuseds.length > 0) { data.unused = unuseds; } - members = []; for (n in member) { if (typeof member[n] === "number") { data.member = member; @@ -23037,7 +24196,7 @@ if (typeof exports === "object" && exports) { exports.JSHINT = JSHINT; } -},{"./lex.js":13,"./messages.js":14,"./options.js":16,"./reg.js":17,"./state.js":18,"./style.js":19,"./vars.js":20,"console-browserify":10,"events":5,"lodash":12}]},{},[]); +},{"./lex.js":13,"./messages.js":14,"./options.js":16,"./reg.js":17,"./scope-manager.js":18,"./state.js":19,"./style.js":20,"./vars.js":21,"console-browserify":10,"events":5,"lodash":12}]},{},[]); JSHINT = require('jshint').JSHINT; if (typeof exports === 'object' && exports) exports.JSHINT = JSHINT; diff --git a/js/node/node_modules/jshint/node_modules/htmlparser2/.travis.yml b/js/node/node_modules/jshint/node_modules/htmlparser2/.travis.yml index 89d4e03fbb..5dfe3637a4 100644 --- a/js/node/node_modules/jshint/node_modules/htmlparser2/.travis.yml +++ b/js/node/node_modules/jshint/node_modules/htmlparser2/.travis.yml @@ -3,4 +3,6 @@ node_js: - 0.10 - 0.11 +sudo: false + script: npm run coveralls diff --git a/js/node/node_modules/jshint/node_modules/htmlparser2/README.md b/js/node/node_modules/jshint/node_modules/htmlparser2/README.md index 9869f6bc6e..8d12a0bb7a 100644 --- a/js/node/node_modules/jshint/node_modules/htmlparser2/README.md +++ b/js/node/node_modules/jshint/node_modules/htmlparser2/README.md @@ -1,18 +1,18 @@ -#htmlparser2 +# htmlparser2 [![NPM version](http://img.shields.io/npm/v/htmlparser2.svg?style=flat)](https://npmjs.org/package/htmlparser2) [![Downloads](https://img.shields.io/npm/dm/htmlparser2.svg?style=flat)](https://npmjs.org/package/htmlparser2) [![Build Status](http://img.shields.io/travis/fb55/htmlparser2/master.svg?style=flat)](http://travis-ci.org/fb55/htmlparser2) [![Coverage](http://img.shields.io/coveralls/fb55/htmlparser2.svg?style=flat)](https://coveralls.io/r/fb55/htmlparser2) -A forgiving HTML/XML/RSS parser written in JS for NodeJS. The parser can handle streams (chunked data) and supports custom handlers for writing custom DOMs/output. +A forgiving HTML/XML/RSS parser. The parser can handle streams and provides a callback interface. -##Installing +## Installation npm install htmlparser2 -A live demo of htmlparser2 is available at http://demos.forbeslindesay.co.uk/htmlparser2/ +A live demo of htmlparser2 is available [here](http://demos.forbeslindesay.co.uk/htmlparser2/). -##Usage +## Usage ```javascript var htmlparser = require("htmlparser2"); @@ -30,7 +30,7 @@ var parser = new htmlparser.Parser({ console.log("That's it?!"); } } -}); +}, {decodeEntities: true}); parser.write("Xyz tag and this