summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/lib')
-rw-r--r--tools/node_modules/eslint/lib/api.js16
-rw-r--r--tools/node_modules/eslint/lib/ast-utils.js1350
-rw-r--r--tools/node_modules/eslint/lib/cli-engine.js715
-rw-r--r--tools/node_modules/eslint/lib/cli.js219
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js659
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/code-path-segment.js245
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/code-path-state.js1440
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/code-path.js234
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js200
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/fork-context.js262
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/id-generator.js46
-rw-r--r--tools/node_modules/eslint/lib/config.js365
-rw-r--r--tools/node_modules/eslint/lib/config/autoconfig.js359
-rw-r--r--tools/node_modules/eslint/lib/config/config-cache.js130
-rw-r--r--tools/node_modules/eslint/lib/config/config-file.js595
-rw-r--r--tools/node_modules/eslint/lib/config/config-initializer.js605
-rw-r--r--tools/node_modules/eslint/lib/config/config-ops.js383
-rw-r--r--tools/node_modules/eslint/lib/config/config-rule.js322
-rw-r--r--tools/node_modules/eslint/lib/config/config-validator.js244
-rw-r--r--tools/node_modules/eslint/lib/config/environments.js84
-rw-r--r--tools/node_modules/eslint/lib/config/plugins.js150
-rw-r--r--tools/node_modules/eslint/lib/file-finder.js145
-rw-r--r--tools/node_modules/eslint/lib/formatters/checkstyle.js60
-rw-r--r--tools/node_modules/eslint/lib/formatters/codeframe.js138
-rw-r--r--tools/node_modules/eslint/lib/formatters/compact.js60
-rw-r--r--tools/node_modules/eslint/lib/formatters/html-template-message.html8
-rw-r--r--tools/node_modules/eslint/lib/formatters/html-template-page.html115
-rw-r--r--tools/node_modules/eslint/lib/formatters/html-template-result.html6
-rw-r--r--tools/node_modules/eslint/lib/formatters/html.js127
-rw-r--r--tools/node_modules/eslint/lib/formatters/jslint-xml.js41
-rw-r--r--tools/node_modules/eslint/lib/formatters/json.js13
-rw-r--r--tools/node_modules/eslint/lib/formatters/junit.js70
-rw-r--r--tools/node_modules/eslint/lib/formatters/stylish.js100
-rw-r--r--tools/node_modules/eslint/lib/formatters/table.js150
-rw-r--r--tools/node_modules/eslint/lib/formatters/tap.js92
-rw-r--r--tools/node_modules/eslint/lib/formatters/unix.js58
-rw-r--r--tools/node_modules/eslint/lib/formatters/visualstudio.js63
-rw-r--r--tools/node_modules/eslint/lib/ignored-paths.js289
-rwxr-xr-xtools/node_modules/eslint/lib/linter.js1123
-rw-r--r--tools/node_modules/eslint/lib/load-rules.js50
-rw-r--r--tools/node_modules/eslint/lib/logging.js28
-rw-r--r--tools/node_modules/eslint/lib/options.js235
-rw-r--r--tools/node_modules/eslint/lib/report-translator.js274
-rw-r--r--tools/node_modules/eslint/lib/rules.js140
-rw-r--r--tools/node_modules/eslint/lib/rules/.eslintrc.yml3
-rw-r--r--tools/node_modules/eslint/lib/rules/accessor-pairs.js156
-rw-r--r--tools/node_modules/eslint/lib/rules/array-bracket-newline.js249
-rw-r--r--tools/node_modules/eslint/lib/rules/array-bracket-spacing.js229
-rw-r--r--tools/node_modules/eslint/lib/rules/array-callback-return.js232
-rw-r--r--tools/node_modules/eslint/lib/rules/array-element-newline.js230
-rw-r--r--tools/node_modules/eslint/lib/rules/arrow-body-style.js215
-rw-r--r--tools/node_modules/eslint/lib/rules/arrow-parens.js156
-rw-r--r--tools/node_modules/eslint/lib/rules/arrow-spacing.js149
-rw-r--r--tools/node_modules/eslint/lib/rules/block-scoped-var.js115
-rw-r--r--tools/node_modules/eslint/lib/rules/block-spacing.js137
-rw-r--r--tools/node_modules/eslint/lib/rules/brace-style.js182
-rw-r--r--tools/node_modules/eslint/lib/rules/callback-return.js175
-rw-r--r--tools/node_modules/eslint/lib/rules/camelcase.js143
-rw-r--r--tools/node_modules/eslint/lib/rules/capitalized-comments.js303
-rw-r--r--tools/node_modules/eslint/lib/rules/class-methods-use-this.js110
-rw-r--r--tools/node_modules/eslint/lib/rules/comma-dangle.js337
-rw-r--r--tools/node_modules/eslint/lib/rules/comma-spacing.js183
-rw-r--r--tools/node_modules/eslint/lib/rules/comma-style.js299
-rw-r--r--tools/node_modules/eslint/lib/rules/complexity.js168
-rw-r--r--tools/node_modules/eslint/lib/rules/computed-property-spacing.js176
-rw-r--r--tools/node_modules/eslint/lib/rules/consistent-return.js188
-rw-r--r--tools/node_modules/eslint/lib/rules/consistent-this.js143
-rw-r--r--tools/node_modules/eslint/lib/rules/constructor-super.js385
-rw-r--r--tools/node_modules/eslint/lib/rules/curly.js399
-rw-r--r--tools/node_modules/eslint/lib/rules/default-case.js90
-rw-r--r--tools/node_modules/eslint/lib/rules/dot-location.js88
-rw-r--r--tools/node_modules/eslint/lib/rules/dot-notation.js159
-rw-r--r--tools/node_modules/eslint/lib/rules/eol-last.js94
-rw-r--r--tools/node_modules/eslint/lib/rules/eqeqeq.js180
-rw-r--r--tools/node_modules/eslint/lib/rules/for-direction.js105
-rw-r--r--tools/node_modules/eslint/lib/rules/func-call-spacing.js159
-rw-r--r--tools/node_modules/eslint/lib/rules/func-name-matching.js193
-rw-r--r--tools/node_modules/eslint/lib/rules/func-names.js114
-rw-r--r--tools/node_modules/eslint/lib/rules/func-style.js89
-rw-r--r--tools/node_modules/eslint/lib/rules/function-paren-newline.js221
-rw-r--r--tools/node_modules/eslint/lib/rules/generator-star-spacing.js199
-rw-r--r--tools/node_modules/eslint/lib/rules/getter-return.js177
-rw-r--r--tools/node_modules/eslint/lib/rules/global-require.js75
-rw-r--r--tools/node_modules/eslint/lib/rules/guard-for-in.js42
-rw-r--r--tools/node_modules/eslint/lib/rules/handle-callback-err.js89
-rw-r--r--tools/node_modules/eslint/lib/rules/id-blacklist.js121
-rw-r--r--tools/node_modules/eslint/lib/rules/id-length.js116
-rw-r--r--tools/node_modules/eslint/lib/rules/id-match.js144
-rw-r--r--tools/node_modules/eslint/lib/rules/implicit-arrow-linebreak.js86
-rw-r--r--tools/node_modules/eslint/lib/rules/indent-legacy.js1137
-rw-r--r--tools/node_modules/eslint/lib/rules/indent.js1522
-rw-r--r--tools/node_modules/eslint/lib/rules/init-declarations.js137
-rw-r--r--tools/node_modules/eslint/lib/rules/jsx-quotes.js89
-rw-r--r--tools/node_modules/eslint/lib/rules/key-spacing.js641
-rw-r--r--tools/node_modules/eslint/lib/rules/keyword-spacing.js584
-rw-r--r--tools/node_modules/eslint/lib/rules/line-comment-position.js115
-rw-r--r--tools/node_modules/eslint/lib/rules/linebreak-style.js96
-rw-r--r--tools/node_modules/eslint/lib/rules/lines-around-comment.js397
-rw-r--r--tools/node_modules/eslint/lib/rules/lines-around-directive.js193
-rw-r--r--tools/node_modules/eslint/lib/rules/lines-between-class-members.js91
-rw-r--r--tools/node_modules/eslint/lib/rules/max-depth.js148
-rw-r--r--tools/node_modules/eslint/lib/rules/max-len.js365
-rw-r--r--tools/node_modules/eslint/lib/rules/max-lines.js144
-rw-r--r--tools/node_modules/eslint/lib/rules/max-nested-callbacks.js112
-rw-r--r--tools/node_modules/eslint/lib/rules/max-params.js96
-rw-r--r--tools/node_modules/eslint/lib/rules/max-statements-per-line.js194
-rw-r--r--tools/node_modules/eslint/lib/rules/max-statements.js170
-rw-r--r--tools/node_modules/eslint/lib/rules/multiline-comment-style.js294
-rw-r--r--tools/node_modules/eslint/lib/rules/multiline-ternary.js89
-rw-r--r--tools/node_modules/eslint/lib/rules/new-cap.js272
-rw-r--r--tools/node_modules/eslint/lib/rules/new-parens.js58
-rw-r--r--tools/node_modules/eslint/lib/rules/newline-after-var.js254
-rw-r--r--tools/node_modules/eslint/lib/rules/newline-before-return.js210
-rw-r--r--tools/node_modules/eslint/lib/rules/newline-per-chained-call.js103
-rw-r--r--tools/node_modules/eslint/lib/rules/no-alert.js123
-rw-r--r--tools/node_modules/eslint/lib/rules/no-array-constructor.js47
-rw-r--r--tools/node_modules/eslint/lib/rules/no-await-in-loop.js83
-rw-r--r--tools/node_modules/eslint/lib/rules/no-bitwise.js111
-rw-r--r--tools/node_modules/eslint/lib/rules/no-buffer-constructor.js37
-rw-r--r--tools/node_modules/eslint/lib/rules/no-caller.js39
-rw-r--r--tools/node_modules/eslint/lib/rules/no-case-declarations.js57
-rw-r--r--tools/node_modules/eslint/lib/rules/no-catch-shadow.js69
-rw-r--r--tools/node_modules/eslint/lib/rules/no-class-assign.js54
-rw-r--r--tools/node_modules/eslint/lib/rules/no-compare-neg-zero.js53
-rw-r--r--tools/node_modules/eslint/lib/rules/no-cond-assign.js139
-rw-r--r--tools/node_modules/eslint/lib/rules/no-confusing-arrow.js76
-rw-r--r--tools/node_modules/eslint/lib/rules/no-console.js131
-rw-r--r--tools/node_modules/eslint/lib/rules/no-const-assign.js47
-rw-r--r--tools/node_modules/eslint/lib/rules/no-constant-condition.js210
-rw-r--r--tools/node_modules/eslint/lib/rules/no-continue.js32
-rw-r--r--tools/node_modules/eslint/lib/rules/no-control-regex.js127
-rw-r--r--tools/node_modules/eslint/lib/rules/no-debugger.js43
-rw-r--r--tools/node_modules/eslint/lib/rules/no-delete-var.js35
-rw-r--r--tools/node_modules/eslint/lib/rules/no-div-regex.js38
-rw-r--r--tools/node_modules/eslint/lib/rules/no-dupe-args.js73
-rw-r--r--tools/node_modules/eslint/lib/rules/no-dupe-class-members.js109
-rw-r--r--tools/node_modules/eslint/lib/rules/no-dupe-keys.js135
-rw-r--r--tools/node_modules/eslint/lib/rules/no-duplicate-case.js43
-rw-r--r--tools/node_modules/eslint/lib/rules/no-duplicate-imports.js137
-rw-r--r--tools/node_modules/eslint/lib/rules/no-else-return.js276
-rw-r--r--tools/node_modules/eslint/lib/rules/no-empty-character-class.js57
-rw-r--r--tools/node_modules/eslint/lib/rules/no-empty-function.js160
-rw-r--r--tools/node_modules/eslint/lib/rules/no-empty-pattern.js36
-rw-r--r--tools/node_modules/eslint/lib/rules/no-empty.js78
-rw-r--r--tools/node_modules/eslint/lib/rules/no-eq-null.js39
-rw-r--r--tools/node_modules/eslint/lib/rules/no-eval.js308
-rw-r--r--tools/node_modules/eslint/lib/rules/no-ex-assign.js45
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extend-native.js174
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extra-bind.js145
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js122
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extra-label.js140
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extra-parens.js745
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extra-semi.js120
-rw-r--r--tools/node_modules/eslint/lib/rules/no-fallthrough.js135
-rw-r--r--tools/node_modules/eslint/lib/rules/no-floating-decimal.js64
-rw-r--r--tools/node_modules/eslint/lib/rules/no-func-assign.js63
-rw-r--r--tools/node_modules/eslint/lib/rules/no-global-assign.js85
-rw-r--r--tools/node_modules/eslint/lib/rules/no-implicit-coercion.js292
-rw-r--r--tools/node_modules/eslint/lib/rules/no-implicit-globals.js55
-rw-r--r--tools/node_modules/eslint/lib/rules/no-implied-eval.js161
-rw-r--r--tools/node_modules/eslint/lib/rules/no-inline-comments.js65
-rw-r--r--tools/node_modules/eslint/lib/rules/no-inner-declarations.js89
-rw-r--r--tools/node_modules/eslint/lib/rules/no-invalid-regexp.js106
-rw-r--r--tools/node_modules/eslint/lib/rules/no-invalid-this.js123
-rw-r--r--tools/node_modules/eslint/lib/rules/no-irregular-whitespace.js236
-rw-r--r--tools/node_modules/eslint/lib/rules/no-iterator.js38
-rw-r--r--tools/node_modules/eslint/lib/rules/no-label-var.js69
-rw-r--r--tools/node_modules/eslint/lib/rules/no-labels.js141
-rw-r--r--tools/node_modules/eslint/lib/rules/no-lone-blocks.js112
-rw-r--r--tools/node_modules/eslint/lib/rules/no-lonely-if.js83
-rw-r--r--tools/node_modules/eslint/lib/rules/no-loop-func.js201
-rw-r--r--tools/node_modules/eslint/lib/rules/no-magic-numbers.js149
-rw-r--r--tools/node_modules/eslint/lib/rules/no-mixed-operators.js209
-rw-r--r--tools/node_modules/eslint/lib/rules/no-mixed-requires.js220
-rw-r--r--tools/node_modules/eslint/lib/rules/no-mixed-spaces-and-tabs.js143
-rw-r--r--tools/node_modules/eslint/lib/rules/no-multi-assign.js41
-rw-r--r--tools/node_modules/eslint/lib/rules/no-multi-spaces.js130
-rw-r--r--tools/node_modules/eslint/lib/rules/no-multi-str.js55
-rw-r--r--tools/node_modules/eslint/lib/rules/no-multiple-empty-lines.js136
-rw-r--r--tools/node_modules/eslint/lib/rules/no-native-reassign.js89
-rw-r--r--tools/node_modules/eslint/lib/rules/no-negated-condition.js82
-rw-r--r--tools/node_modules/eslint/lib/rules/no-negated-in-lhs.js38
-rw-r--r--tools/node_modules/eslint/lib/rules/no-nested-ternary.js34
-rw-r--r--tools/node_modules/eslint/lib/rules/no-new-func.js45
-rw-r--r--tools/node_modules/eslint/lib/rules/no-new-object.js35
-rw-r--r--tools/node_modules/eslint/lib/rules/no-new-require.js35
-rw-r--r--tools/node_modules/eslint/lib/rules/no-new-symbol.js43
-rw-r--r--tools/node_modules/eslint/lib/rules/no-new-wrappers.js37
-rw-r--r--tools/node_modules/eslint/lib/rules/no-new.js33
-rw-r--r--tools/node_modules/eslint/lib/rules/no-obj-calls.js39
-rw-r--r--tools/node_modules/eslint/lib/rules/no-octal-escape.js47
-rw-r--r--tools/node_modules/eslint/lib/rules/no-octal.js35
-rw-r--r--tools/node_modules/eslint/lib/rules/no-param-reassign.js173
-rw-r--r--tools/node_modules/eslint/lib/rules/no-path-concat.js49
-rw-r--r--tools/node_modules/eslint/lib/rules/no-plusplus.js61
-rw-r--r--tools/node_modules/eslint/lib/rules/no-process-env.js39
-rw-r--r--tools/node_modules/eslint/lib/rules/no-process-exit.js35
-rw-r--r--tools/node_modules/eslint/lib/rules/no-proto.js38
-rw-r--r--tools/node_modules/eslint/lib/rules/no-prototype-builtins.js54
-rw-r--r--tools/node_modules/eslint/lib/rules/no-redeclare.js101
-rw-r--r--tools/node_modules/eslint/lib/rules/no-regex-spaces.js114
-rw-r--r--tools/node_modules/eslint/lib/rules/no-restricted-globals.js120
-rw-r--r--tools/node_modules/eslint/lib/rules/no-restricted-imports.js263
-rw-r--r--tools/node_modules/eslint/lib/rules/no-restricted-modules.js177
-rw-r--r--tools/node_modules/eslint/lib/rules/no-restricted-properties.js173
-rw-r--r--tools/node_modules/eslint/lib/rules/no-restricted-syntax.js62
-rw-r--r--tools/node_modules/eslint/lib/rules/no-return-assign.js71
-rw-r--r--tools/node_modules/eslint/lib/rules/no-return-await.js94
-rw-r--r--tools/node_modules/eslint/lib/rules/no-script-url.js41
-rw-r--r--tools/node_modules/eslint/lib/rules/no-self-assign.js214
-rw-r--r--tools/node_modules/eslint/lib/rules/no-self-compare.js53
-rw-r--r--tools/node_modules/eslint/lib/rules/no-sequences.js112
-rw-r--r--tools/node_modules/eslint/lib/rules/no-shadow-restricted-names.js69
-rw-r--r--tools/node_modules/eslint/lib/rules/no-shadow.js188
-rw-r--r--tools/node_modules/eslint/lib/rules/no-spaced-func.js75
-rw-r--r--tools/node_modules/eslint/lib/rules/no-sparse-arrays.js43
-rw-r--r--tools/node_modules/eslint/lib/rules/no-sync.js53
-rw-r--r--tools/node_modules/eslint/lib/rules/no-tabs.js47
-rw-r--r--tools/node_modules/eslint/lib/rules/no-template-curly-in-string.js37
-rw-r--r--tools/node_modules/eslint/lib/rules/no-ternary.js34
-rw-r--r--tools/node_modules/eslint/lib/rules/no-this-before-super.js299
-rw-r--r--tools/node_modules/eslint/lib/rules/no-throw-literal.js43
-rw-r--r--tools/node_modules/eslint/lib/rules/no-trailing-spaces.js169
-rw-r--r--tools/node_modules/eslint/lib/rules/no-undef-init.js63
-rw-r--r--tools/node_modules/eslint/lib/rules/no-undef.js71
-rw-r--r--tools/node_modules/eslint/lib/rules/no-undefined.js77
-rw-r--r--tools/node_modules/eslint/lib/rules/no-underscore-dangle.js203
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unexpected-multiline.js98
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unmodified-loop-condition.js366
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unneeded-ternary.js155
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unreachable.js212
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unsafe-finally.js104
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unsafe-negation.js80
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unused-expressions.js126
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unused-labels.js106
-rw-r--r--tools/node_modules/eslint/lib/rules/no-unused-vars.js647
-rw-r--r--tools/node_modules/eslint/lib/rules/no-use-before-define.js266
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-call.js80
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-computed-key.js75
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-concat.js108
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-constructor.js182
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-escape.js223
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-rename.js147
-rw-r--r--tools/node_modules/eslint/lib/rules/no-useless-return.js304
-rw-r--r--tools/node_modules/eslint/lib/rules/no-var.js328
-rw-r--r--tools/node_modules/eslint/lib/rules/no-void.js37
-rw-r--r--tools/node_modules/eslint/lib/rules/no-warning-comments.js139
-rw-r--r--tools/node_modules/eslint/lib/rules/no-whitespace-before-property.js94
-rw-r--r--tools/node_modules/eslint/lib/rules/no-with.js32
-rw-r--r--tools/node_modules/eslint/lib/rules/nonblock-statement-body-position.js114
-rw-r--r--tools/node_modules/eslint/lib/rules/object-curly-newline.js249
-rw-r--r--tools/node_modules/eslint/lib/rules/object-curly-spacing.js299
-rw-r--r--tools/node_modules/eslint/lib/rules/object-property-newline.js84
-rw-r--r--tools/node_modules/eslint/lib/rules/object-shorthand.js454
-rw-r--r--tools/node_modules/eslint/lib/rules/one-var-declaration-per-line.js86
-rw-r--r--tools/node_modules/eslint/lib/rules/one-var.js367
-rw-r--r--tools/node_modules/eslint/lib/rules/operator-assignment.js206
-rw-r--r--tools/node_modules/eslint/lib/rules/operator-linebreak.js252
-rw-r--r--tools/node_modules/eslint/lib/rules/padded-blocks.js256
-rw-r--r--tools/node_modules/eslint/lib/rules/padding-line-between-statements.js589
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-arrow-callback.js304
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-const.js321
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-destructuring.js217
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-numeric-literals.js112
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-promise-reject-errors.js124
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-reflect.js119
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-rest-params.js111
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-spread.js96
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-template.js232
-rw-r--r--tools/node_modules/eslint/lib/rules/quote-props.js298
-rw-r--r--tools/node_modules/eslint/lib/rules/quotes.js296
-rw-r--r--tools/node_modules/eslint/lib/rules/radix.js171
-rw-r--r--tools/node_modules/eslint/lib/rules/require-await.js95
-rw-r--r--tools/node_modules/eslint/lib/rules/require-jsdoc.js105
-rw-r--r--tools/node_modules/eslint/lib/rules/require-yield.js71
-rw-r--r--tools/node_modules/eslint/lib/rules/rest-spread-spacing.js107
-rw-r--r--tools/node_modules/eslint/lib/rules/semi-spacing.js211
-rw-r--r--tools/node_modules/eslint/lib/rules/semi-style.js143
-rw-r--r--tools/node_modules/eslint/lib/rules/semi.js325
-rw-r--r--tools/node_modules/eslint/lib/rules/sort-imports.js196
-rw-r--r--tools/node_modules/eslint/lib/rules/sort-keys.js157
-rw-r--r--tools/node_modules/eslint/lib/rules/sort-vars.js96
-rw-r--r--tools/node_modules/eslint/lib/rules/space-before-blocks.js148
-rw-r--r--tools/node_modules/eslint/lib/rules/space-before-function-paren.js142
-rw-r--r--tools/node_modules/eslint/lib/rules/space-in-parens.js274
-rw-r--r--tools/node_modules/eslint/lib/rules/space-infix-ops.js167
-rw-r--r--tools/node_modules/eslint/lib/rules/space-unary-ops.js319
-rw-r--r--tools/node_modules/eslint/lib/rules/spaced-comment.js375
-rw-r--r--tools/node_modules/eslint/lib/rules/strict.js277
-rw-r--r--tools/node_modules/eslint/lib/rules/switch-colon-spacing.js133
-rw-r--r--tools/node_modules/eslint/lib/rules/symbol-description.js66
-rw-r--r--tools/node_modules/eslint/lib/rules/template-curly-spacing.js121
-rwxr-xr-xtools/node_modules/eslint/lib/rules/template-tag-spacing.js77
-rw-r--r--tools/node_modules/eslint/lib/rules/unicode-bom.js66
-rw-r--r--tools/node_modules/eslint/lib/rules/use-isnan.js34
-rw-r--r--tools/node_modules/eslint/lib/rules/valid-jsdoc.js423
-rw-r--r--tools/node_modules/eslint/lib/rules/valid-typeof.js77
-rw-r--r--tools/node_modules/eslint/lib/rules/vars-on-top.js149
-rw-r--r--tools/node_modules/eslint/lib/rules/wrap-iife.js151
-rw-r--r--tools/node_modules/eslint/lib/rules/wrap-regex.js52
-rw-r--r--tools/node_modules/eslint/lib/rules/yield-star-spacing.js117
-rw-r--r--tools/node_modules/eslint/lib/rules/yoda.js310
-rw-r--r--tools/node_modules/eslint/lib/testers/rule-tester.js558
-rw-r--r--tools/node_modules/eslint/lib/timing.js141
-rw-r--r--tools/node_modules/eslint/lib/token-store/backward-token-comment-cursor.js57
-rw-r--r--tools/node_modules/eslint/lib/token-store/backward-token-cursor.js58
-rw-r--r--tools/node_modules/eslint/lib/token-store/cursor.js76
-rw-r--r--tools/node_modules/eslint/lib/token-store/cursors.js92
-rw-r--r--tools/node_modules/eslint/lib/token-store/decorative-cursor.js39
-rw-r--r--tools/node_modules/eslint/lib/token-store/filter-cursor.js43
-rw-r--r--tools/node_modules/eslint/lib/token-store/forward-token-comment-cursor.js57
-rw-r--r--tools/node_modules/eslint/lib/token-store/forward-token-cursor.js63
-rw-r--r--tools/node_modules/eslint/lib/token-store/index.js633
-rw-r--r--tools/node_modules/eslint/lib/token-store/limit-cursor.js40
-rw-r--r--tools/node_modules/eslint/lib/token-store/padded-token-cursor.js38
-rw-r--r--tools/node_modules/eslint/lib/token-store/skip-cursor.js42
-rw-r--r--tools/node_modules/eslint/lib/token-store/utils.js104
-rw-r--r--tools/node_modules/eslint/lib/util/ajv.js29
-rw-r--r--tools/node_modules/eslint/lib/util/apply-disable-directives.js160
-rw-r--r--tools/node_modules/eslint/lib/util/fix-tracker.js120
-rw-r--r--tools/node_modules/eslint/lib/util/glob-util.js182
-rw-r--r--tools/node_modules/eslint/lib/util/glob.js63
-rw-r--r--tools/node_modules/eslint/lib/util/hash.js35
-rw-r--r--tools/node_modules/eslint/lib/util/keywords.js67
-rw-r--r--tools/node_modules/eslint/lib/util/module-resolver.js85
-rw-r--r--tools/node_modules/eslint/lib/util/naming.js112
-rw-r--r--tools/node_modules/eslint/lib/util/node-event-generator.js308
-rw-r--r--tools/node_modules/eslint/lib/util/npm-util.js179
-rw-r--r--tools/node_modules/eslint/lib/util/path-util.js74
-rw-r--r--tools/node_modules/eslint/lib/util/patterns/letters.js36
-rw-r--r--tools/node_modules/eslint/lib/util/rule-fixer.js140
-rw-r--r--tools/node_modules/eslint/lib/util/safe-emitter.js54
-rw-r--r--tools/node_modules/eslint/lib/util/source-code-fixer.js152
-rw-r--r--tools/node_modules/eslint/lib/util/source-code-util.js109
-rw-r--r--tools/node_modules/eslint/lib/util/source-code.js472
-rw-r--r--tools/node_modules/eslint/lib/util/traverser.js45
-rw-r--r--tools/node_modules/eslint/lib/util/xml-escape.js34
337 files changed, 57670 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/lib/api.js b/tools/node_modules/eslint/lib/api.js
new file mode 100644
index 0000000000..0a0832a476
--- /dev/null
+++ b/tools/node_modules/eslint/lib/api.js
@@ -0,0 +1,16 @@
+/**
+ * @fileoverview Expose out ESLint and CLI to require.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+const Linter = require("./linter");
+
+module.exports = {
+ linter: new Linter(),
+ Linter,
+ CLIEngine: require("./cli-engine"),
+ RuleTester: require("./testers/rule-tester"),
+ SourceCode: require("./util/source-code")
+};
diff --git a/tools/node_modules/eslint/lib/ast-utils.js b/tools/node_modules/eslint/lib/ast-utils.js
new file mode 100644
index 0000000000..a186bdee54
--- /dev/null
+++ b/tools/node_modules/eslint/lib/ast-utils.js
@@ -0,0 +1,1350 @@
+/**
+ * @fileoverview Common utils for AST.
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const esutils = require("esutils");
+const espree = require("espree");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/;
+const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/;
+const arrayOrTypedArrayPattern = /Array$/;
+const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/;
+const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/;
+const breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/;
+const thisTagPattern = /^[\s*]*@this/m;
+
+
+const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/;
+const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
+const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/;
+const SHEBANG_MATCHER = /^#!([^\r\n]+)/;
+
+// A set of node types that can contain a list of statements
+const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
+
+/**
+ * Checks reference if is non initializer and writable.
+ * @param {Reference} reference - A reference to check.
+ * @param {int} index - The index of the reference in the references.
+ * @param {Reference[]} references - The array that the reference belongs to.
+ * @returns {boolean} Success/Failure
+ * @private
+ */
+function isModifyingReference(reference, index, references) {
+ const identifier = reference.identifier;
+
+ /*
+ * Destructuring assignments can have multiple default value, so
+ * possibly there are multiple writeable references for the same
+ * identifier.
+ */
+ const modifyingDifferentIdentifier = index === 0 ||
+ references[index - 1].identifier !== identifier;
+
+ return (identifier &&
+ reference.init === false &&
+ reference.isWrite() &&
+ modifyingDifferentIdentifier
+ );
+}
+
+/**
+ * Checks whether the given string starts with uppercase or not.
+ *
+ * @param {string} s - The string to check.
+ * @returns {boolean} `true` if the string starts with uppercase.
+ */
+function startsWithUpperCase(s) {
+ return s[0] !== s[0].toLocaleLowerCase();
+}
+
+/**
+ * Checks whether or not a node is a constructor.
+ * @param {ASTNode} node - A function node to check.
+ * @returns {boolean} Wehether or not a node is a constructor.
+ */
+function isES5Constructor(node) {
+ return (node.id && startsWithUpperCase(node.id.name));
+}
+
+/**
+ * Finds a function node from ancestors of a node.
+ * @param {ASTNode} node - A start node to find.
+ * @returns {Node|null} A found function node.
+ */
+function getUpperFunction(node) {
+ while (node) {
+ if (anyFunctionPattern.test(node.type)) {
+ return node;
+ }
+ node = node.parent;
+ }
+ return null;
+}
+
+/**
+ * Checks whether a given node is a function node or not.
+ * The following types are function nodes:
+ *
+ * - ArrowFunctionExpression
+ * - FunctionDeclaration
+ * - FunctionExpression
+ *
+ * @param {ASTNode|null} node - A node to check.
+ * @returns {boolean} `true` if the node is a function node.
+ */
+function isFunction(node) {
+ return Boolean(node && anyFunctionPattern.test(node.type));
+}
+
+/**
+ * Checks whether a given node is a loop node or not.
+ * The following types are loop nodes:
+ *
+ * - DoWhileStatement
+ * - ForInStatement
+ * - ForOfStatement
+ * - ForStatement
+ * - WhileStatement
+ *
+ * @param {ASTNode|null} node - A node to check.
+ * @returns {boolean} `true` if the node is a loop node.
+ */
+function isLoop(node) {
+ return Boolean(node && anyLoopPattern.test(node.type));
+}
+
+/**
+ * Checks whether the given node is in a loop or not.
+ *
+ * @param {ASTNode} node - The node to check.
+ * @returns {boolean} `true` if the node is in a loop.
+ */
+function isInLoop(node) {
+ while (node && !isFunction(node)) {
+ if (isLoop(node)) {
+ return true;
+ }
+
+ node = node.parent;
+ }
+
+ return false;
+}
+
+/**
+ * Checks whether or not a node is `null` or `undefined`.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a `null` or `undefined`.
+ * @public
+ */
+function isNullOrUndefined(node) {
+ return (
+ module.exports.isNullLiteral(node) ||
+ (node.type === "Identifier" && node.name === "undefined") ||
+ (node.type === "UnaryExpression" && node.operator === "void")
+ );
+}
+
+/**
+ * Checks whether or not a node is callee.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is callee.
+ */
+function isCallee(node) {
+ return node.parent.type === "CallExpression" && node.parent.callee === node;
+}
+
+/**
+ * Checks whether or not a node is `Reflect.apply`.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a `Reflect.apply`.
+ */
+function isReflectApply(node) {
+ return (
+ node.type === "MemberExpression" &&
+ node.object.type === "Identifier" &&
+ node.object.name === "Reflect" &&
+ node.property.type === "Identifier" &&
+ node.property.name === "apply" &&
+ node.computed === false
+ );
+}
+
+/**
+ * Checks whether or not a node is `Array.from`.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a `Array.from`.
+ */
+function isArrayFromMethod(node) {
+ return (
+ node.type === "MemberExpression" &&
+ node.object.type === "Identifier" &&
+ arrayOrTypedArrayPattern.test(node.object.name) &&
+ node.property.type === "Identifier" &&
+ node.property.name === "from" &&
+ node.computed === false
+ );
+}
+
+/**
+ * Checks whether or not a node is a method which has `thisArg`.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a method which has `thisArg`.
+ */
+function isMethodWhichHasThisArg(node) {
+ while (node) {
+ if (node.type === "Identifier") {
+ return arrayMethodPattern.test(node.name);
+ }
+ if (node.type === "MemberExpression" && !node.computed) {
+ node = node.property;
+ continue;
+ }
+
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * Creates the negate function of the given function.
+ * @param {Function} f - The function to negate.
+ * @returns {Function} Negated function.
+ */
+function negate(f) {
+ return token => !f(token);
+}
+
+/**
+ * Checks whether or not a node has a `@this` tag in its comments.
+ * @param {ASTNode} node - A node to check.
+ * @param {SourceCode} sourceCode - A SourceCode instance to get comments.
+ * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
+ */
+function hasJSDocThisTag(node, sourceCode) {
+ const jsdocComment = sourceCode.getJSDocComment(node);
+
+ if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
+ return true;
+ }
+
+ // Checks `@this` in its leading comments for callbacks,
+ // because callbacks don't have its JSDoc comment.
+ // e.g.
+ // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
+ return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
+}
+
+/**
+ * Determines if a node is surrounded by parentheses.
+ * @param {SourceCode} sourceCode The ESLint source code object
+ * @param {ASTNode} node The node to be checked.
+ * @returns {boolean} True if the node is parenthesised.
+ * @private
+ */
+function isParenthesised(sourceCode, node) {
+ const previousToken = sourceCode.getTokenBefore(node),
+ nextToken = sourceCode.getTokenAfter(node);
+
+ return Boolean(previousToken && nextToken) &&
+ previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
+ nextToken.value === ")" && nextToken.range[0] >= node.range[1];
+}
+
+/**
+ * Checks if the given token is an arrow token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is an arrow token.
+ */
+function isArrowToken(token) {
+ return token.value === "=>" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a comma token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a comma token.
+ */
+function isCommaToken(token) {
+ return token.value === "," && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a semicolon token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a semicolon token.
+ */
+function isSemicolonToken(token) {
+ return token.value === ";" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a colon token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a colon token.
+ */
+function isColonToken(token) {
+ return token.value === ":" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is an opening parenthesis token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is an opening parenthesis token.
+ */
+function isOpeningParenToken(token) {
+ return token.value === "(" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a closing parenthesis token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a closing parenthesis token.
+ */
+function isClosingParenToken(token) {
+ return token.value === ")" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is an opening square bracket token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is an opening square bracket token.
+ */
+function isOpeningBracketToken(token) {
+ return token.value === "[" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a closing square bracket token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a closing square bracket token.
+ */
+function isClosingBracketToken(token) {
+ return token.value === "]" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is an opening brace token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is an opening brace token.
+ */
+function isOpeningBraceToken(token) {
+ return token.value === "{" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a closing brace token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a closing brace token.
+ */
+function isClosingBraceToken(token) {
+ return token.value === "}" && token.type === "Punctuator";
+}
+
+/**
+ * Checks if the given token is a comment token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a comment token.
+ */
+function isCommentToken(token) {
+ return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
+}
+
+/**
+ * Checks if the given token is a keyword token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a keyword token.
+ */
+function isKeywordToken(token) {
+ return token.type === "Keyword";
+}
+
+/**
+ * Gets the `(` token of the given function node.
+ *
+ * @param {ASTNode} node - The function node to get.
+ * @param {SourceCode} sourceCode - The source code object to get tokens.
+ * @returns {Token} `(` token.
+ */
+function getOpeningParenOfParams(node, sourceCode) {
+ return node.id
+ ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
+ : sourceCode.getFirstToken(node, isOpeningParenToken);
+}
+
+/**
+ * Creates a version of the LINEBREAK_MATCHER regex with the global flag.
+ * Global regexes are mutable, so this needs to be a function instead of a constant.
+ * @returns {RegExp} A global regular expression that matches line terminators
+ */
+function createGlobalLinebreakMatcher() {
+ return new RegExp(LINEBREAK_MATCHER.source, "g");
+}
+
+/**
+ * Checks whether or not the tokens of two given nodes are same.
+ * @param {ASTNode} left - A node 1 to compare.
+ * @param {ASTNode} right - A node 2 to compare.
+ * @param {SourceCode} sourceCode - The ESLint source code object.
+ * @returns {boolean} the source code for the given node.
+ */
+function equalTokens(left, right, sourceCode) {
+ const tokensL = sourceCode.getTokens(left);
+ const tokensR = sourceCode.getTokens(right);
+
+ if (tokensL.length !== tokensR.length) {
+ return false;
+ }
+ for (let i = 0; i < tokensL.length; ++i) {
+ if (tokensL[i].type !== tokensR[i].type ||
+ tokensL[i].value !== tokensR[i].value
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ COMMENTS_IGNORE_PATTERN,
+ LINEBREAKS,
+ LINEBREAK_MATCHER,
+ SHEBANG_MATCHER,
+ STATEMENT_LIST_PARENTS,
+
+ /**
+ * Determines whether two adjacent tokens are on the same line.
+ * @param {Object} left - The left token object.
+ * @param {Object} right - The right token object.
+ * @returns {boolean} Whether or not the tokens are on the same line.
+ * @public
+ */
+ isTokenOnSameLine(left, right) {
+ return left.loc.end.line === right.loc.start.line;
+ },
+
+ isNullOrUndefined,
+ isCallee,
+ isES5Constructor,
+ getUpperFunction,
+ isFunction,
+ isLoop,
+ isInLoop,
+ isArrayFromMethod,
+ isParenthesised,
+ createGlobalLinebreakMatcher,
+ equalTokens,
+
+ isArrowToken,
+ isClosingBraceToken,
+ isClosingBracketToken,
+ isClosingParenToken,
+ isColonToken,
+ isCommaToken,
+ isCommentToken,
+ isKeywordToken,
+ isNotClosingBraceToken: negate(isClosingBraceToken),
+ isNotClosingBracketToken: negate(isClosingBracketToken),
+ isNotClosingParenToken: negate(isClosingParenToken),
+ isNotColonToken: negate(isColonToken),
+ isNotCommaToken: negate(isCommaToken),
+ isNotOpeningBraceToken: negate(isOpeningBraceToken),
+ isNotOpeningBracketToken: negate(isOpeningBracketToken),
+ isNotOpeningParenToken: negate(isOpeningParenToken),
+ isNotSemicolonToken: negate(isSemicolonToken),
+ isOpeningBraceToken,
+ isOpeningBracketToken,
+ isOpeningParenToken,
+ isSemicolonToken,
+
+ /**
+ * Checks whether or not a given node is a string literal.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a string literal.
+ */
+ isStringLiteral(node) {
+ return (
+ (node.type === "Literal" && typeof node.value === "string") ||
+ node.type === "TemplateLiteral"
+ );
+ },
+
+ /**
+ * Checks whether a given node is a breakable statement or not.
+ * The node is breakable if the node is one of the following type:
+ *
+ * - DoWhileStatement
+ * - ForInStatement
+ * - ForOfStatement
+ * - ForStatement
+ * - SwitchStatement
+ * - WhileStatement
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is breakable.
+ */
+ isBreakableStatement(node) {
+ return breakableTypePattern.test(node.type);
+ },
+
+ /**
+ * Gets the label if the parent node of a given node is a LabeledStatement.
+ *
+ * @param {ASTNode} node - A node to get.
+ * @returns {string|null} The label or `null`.
+ */
+ getLabel(node) {
+ if (node.parent.type === "LabeledStatement") {
+ return node.parent.label.name;
+ }
+ return null;
+ },
+
+ /**
+ * Gets references which are non initializer and writable.
+ * @param {Reference[]} references - An array of references.
+ * @returns {Reference[]} An array of only references which are non initializer and writable.
+ * @public
+ */
+ getModifyingReferences(references) {
+ return references.filter(isModifyingReference);
+ },
+
+ /**
+ * Validate that a string passed in is surrounded by the specified character
+ * @param {string} val The text to check.
+ * @param {string} character The character to see if it's surrounded by.
+ * @returns {boolean} True if the text is surrounded by the character, false if not.
+ * @private
+ */
+ isSurroundedBy(val, character) {
+ return val[0] === character && val[val.length - 1] === character;
+ },
+
+ /**
+ * Returns whether the provided node is an ESLint directive comment or not
+ * @param {Line|Block} node The comment token to be checked
+ * @returns {boolean} `true` if the node is an ESLint directive comment
+ */
+ isDirectiveComment(node) {
+ const comment = node.value.trim();
+
+ return (
+ node.type === "Line" && comment.indexOf("eslint-") === 0 ||
+ node.type === "Block" && (
+ comment.indexOf("global ") === 0 ||
+ comment.indexOf("eslint ") === 0 ||
+ comment.indexOf("eslint-") === 0
+ )
+ );
+ },
+
+ /**
+ * Gets the trailing statement of a given node.
+ *
+ * if (code)
+ * consequent;
+ *
+ * When taking this `IfStatement`, returns `consequent;` statement.
+ *
+ * @param {ASTNode} A node to get.
+ * @returns {ASTNode|null} The trailing statement's node.
+ */
+ getTrailingStatement: esutils.ast.trailingStatement,
+
+ /**
+ * Finds the variable by a given name in a given scope and its upper scopes.
+ *
+ * @param {eslint-scope.Scope} initScope - A scope to start find.
+ * @param {string} name - A variable name to find.
+ * @returns {eslint-scope.Variable|null} A found variable or `null`.
+ */
+ getVariableByName(initScope, name) {
+ let scope = initScope;
+
+ while (scope) {
+ const variable = scope.set.get(name);
+
+ if (variable) {
+ return variable;
+ }
+
+ scope = scope.upper;
+ }
+
+ return null;
+ },
+
+ /**
+ * Checks whether or not a given function node is the default `this` binding.
+ *
+ * First, this checks the node:
+ *
+ * - The function name does not start with uppercase (it's a constructor).
+ * - The function does not have a JSDoc comment that has a @this tag.
+ *
+ * Next, this checks the location of the node.
+ * If the location is below, this judges `this` is valid.
+ *
+ * - The location is not on an object literal.
+ * - The location is not assigned to a variable which starts with an uppercase letter.
+ * - The location is not on an ES2015 class.
+ * - Its `bind`/`call`/`apply` method is not called directly.
+ * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
+ *
+ * @param {ASTNode} node - A function node to check.
+ * @param {SourceCode} sourceCode - A SourceCode instance to get comments.
+ * @returns {boolean} The function node is the default `this` binding.
+ */
+ isDefaultThisBinding(node, sourceCode) {
+ if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) {
+ return false;
+ }
+ const isAnonymous = node.id === null;
+
+ while (node) {
+ const parent = node.parent;
+
+ switch (parent.type) {
+
+ /*
+ * Looks up the destination.
+ * e.g., obj.foo = nativeFoo || function foo() { ... };
+ */
+ case "LogicalExpression":
+ case "ConditionalExpression":
+ node = parent;
+ break;
+
+ /*
+ * If the upper function is IIFE, checks the destination of the return value.
+ * e.g.
+ * obj.foo = (function() {
+ * // setup...
+ * return function foo() { ... };
+ * })();
+ * obj.foo = (() =>
+ * function foo() { ... }
+ * )();
+ */
+ case "ReturnStatement": {
+ const func = getUpperFunction(parent);
+
+ if (func === null || !isCallee(func)) {
+ return true;
+ }
+ node = func.parent;
+ break;
+ }
+ case "ArrowFunctionExpression":
+ if (node !== parent.body || !isCallee(parent)) {
+ return true;
+ }
+ node = parent.parent;
+ break;
+
+ /*
+ * e.g.
+ * var obj = { foo() { ... } };
+ * var obj = { foo: function() { ... } };
+ * class A { constructor() { ... } }
+ * class A { foo() { ... } }
+ * class A { get foo() { ... } }
+ * class A { set foo() { ... } }
+ * class A { static foo() { ... } }
+ */
+ case "Property":
+ case "MethodDefinition":
+ return parent.value !== node;
+
+ /*
+ * e.g.
+ * obj.foo = function foo() { ... };
+ * Foo = function() { ... };
+ * [obj.foo = function foo() { ... }] = a;
+ * [Foo = function() { ... }] = a;
+ */
+ case "AssignmentExpression":
+ case "AssignmentPattern":
+ if (parent.left.type === "MemberExpression") {
+ return false;
+ }
+ if (
+ isAnonymous &&
+ parent.left.type === "Identifier" &&
+ startsWithUpperCase(parent.left.name)
+ ) {
+ return false;
+ }
+ return true;
+
+ /*
+ * e.g.
+ * var Foo = function() { ... };
+ */
+ case "VariableDeclarator":
+ return !(
+ isAnonymous &&
+ parent.init === node &&
+ parent.id.type === "Identifier" &&
+ startsWithUpperCase(parent.id.name)
+ );
+
+ /*
+ * e.g.
+ * var foo = function foo() { ... }.bind(obj);
+ * (function foo() { ... }).call(obj);
+ * (function foo() { ... }).apply(obj, []);
+ */
+ case "MemberExpression":
+ return (
+ parent.object !== node ||
+ parent.property.type !== "Identifier" ||
+ !bindOrCallOrApplyPattern.test(parent.property.name) ||
+ !isCallee(parent) ||
+ parent.parent.arguments.length === 0 ||
+ isNullOrUndefined(parent.parent.arguments[0])
+ );
+
+ /*
+ * e.g.
+ * Reflect.apply(function() {}, obj, []);
+ * Array.from([], function() {}, obj);
+ * list.forEach(function() {}, obj);
+ */
+ case "CallExpression":
+ if (isReflectApply(parent.callee)) {
+ return (
+ parent.arguments.length !== 3 ||
+ parent.arguments[0] !== node ||
+ isNullOrUndefined(parent.arguments[1])
+ );
+ }
+ if (isArrayFromMethod(parent.callee)) {
+ return (
+ parent.arguments.length !== 3 ||
+ parent.arguments[1] !== node ||
+ isNullOrUndefined(parent.arguments[2])
+ );
+ }
+ if (isMethodWhichHasThisArg(parent.callee)) {
+ return (
+ parent.arguments.length !== 2 ||
+ parent.arguments[0] !== node ||
+ isNullOrUndefined(parent.arguments[1])
+ );
+ }
+ return true;
+
+ // Otherwise `this` is default.
+ default:
+ return true;
+ }
+ }
+
+ /* istanbul ignore next */
+ return true;
+ },
+
+ /**
+ * Get the precedence level based on the node type
+ * @param {ASTNode} node node to evaluate
+ * @returns {int} precedence level
+ * @private
+ */
+ getPrecedence(node) {
+ switch (node.type) {
+ case "SequenceExpression":
+ return 0;
+
+ case "AssignmentExpression":
+ case "ArrowFunctionExpression":
+ case "YieldExpression":
+ return 1;
+
+ case "ConditionalExpression":
+ return 3;
+
+ case "LogicalExpression":
+ switch (node.operator) {
+ case "||":
+ return 4;
+ case "&&":
+ return 5;
+
+ // no default
+ }
+
+ /* falls through */
+
+ case "BinaryExpression":
+
+ switch (node.operator) {
+ case "|":
+ return 6;
+ case "^":
+ return 7;
+ case "&":
+ return 8;
+ case "==":
+ case "!=":
+ case "===":
+ case "!==":
+ return 9;
+ case "<":
+ case "<=":
+ case ">":
+ case ">=":
+ case "in":
+ case "instanceof":
+ return 10;
+ case "<<":
+ case ">>":
+ case ">>>":
+ return 11;
+ case "+":
+ case "-":
+ return 12;
+ case "*":
+ case "/":
+ case "%":
+ return 13;
+ case "**":
+ return 15;
+
+ // no default
+ }
+
+ /* falls through */
+
+ case "UnaryExpression":
+ case "AwaitExpression":
+ return 16;
+
+ case "UpdateExpression":
+ return 17;
+
+ case "CallExpression":
+ return 18;
+
+ case "NewExpression":
+ return 19;
+
+ default:
+ return 20;
+ }
+ },
+
+ /**
+ * Checks whether the given node is an empty block node or not.
+ *
+ * @param {ASTNode|null} node - The node to check.
+ * @returns {boolean} `true` if the node is an empty block.
+ */
+ isEmptyBlock(node) {
+ return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
+ },
+
+ /**
+ * Checks whether the given node is an empty function node or not.
+ *
+ * @param {ASTNode|null} node - The node to check.
+ * @returns {boolean} `true` if the node is an empty function.
+ */
+ isEmptyFunction(node) {
+ return isFunction(node) && module.exports.isEmptyBlock(node.body);
+ },
+
+ /**
+ * Gets the property name of a given node.
+ * The node can be a MemberExpression, a Property, or a MethodDefinition.
+ *
+ * If the name is dynamic, this returns `null`.
+ *
+ * For examples:
+ *
+ * a.b // => "b"
+ * a["b"] // => "b"
+ * a['b'] // => "b"
+ * a[`b`] // => "b"
+ * a[100] // => "100"
+ * a[b] // => null
+ * a["a" + "b"] // => null
+ * a[tag`b`] // => null
+ * a[`${b}`] // => null
+ *
+ * let a = {b: 1} // => "b"
+ * let a = {["b"]: 1} // => "b"
+ * let a = {['b']: 1} // => "b"
+ * let a = {[`b`]: 1} // => "b"
+ * let a = {[100]: 1} // => "100"
+ * let a = {[b]: 1} // => null
+ * let a = {["a" + "b"]: 1} // => null
+ * let a = {[tag`b`]: 1} // => null
+ * let a = {[`${b}`]: 1} // => null
+ *
+ * @param {ASTNode} node - The node to get.
+ * @returns {string|null} The property name if static. Otherwise, null.
+ */
+ getStaticPropertyName(node) {
+ let prop;
+
+ switch (node && node.type) {
+ case "Property":
+ case "MethodDefinition":
+ prop = node.key;
+ break;
+
+ case "MemberExpression":
+ prop = node.property;
+ break;
+
+ // no default
+ }
+
+ switch (prop && prop.type) {
+ case "Literal":
+ return String(prop.value);
+
+ case "TemplateLiteral":
+ if (prop.expressions.length === 0 && prop.quasis.length === 1) {
+ return prop.quasis[0].value.cooked;
+ }
+ break;
+
+ case "Identifier":
+ if (!node.computed) {
+ return prop.name;
+ }
+ break;
+
+ // no default
+ }
+
+ return null;
+ },
+
+ /**
+ * Get directives from directive prologue of a Program or Function node.
+ * @param {ASTNode} node - The node to check.
+ * @returns {ASTNode[]} The directives found in the directive prologue.
+ */
+ getDirectivePrologue(node) {
+ const directives = [];
+
+ // Directive prologues only occur at the top of files or functions.
+ if (
+ node.type === "Program" ||
+ node.type === "FunctionDeclaration" ||
+ node.type === "FunctionExpression" ||
+
+ /*
+ * Do not check arrow functions with implicit return.
+ * `() => "use strict";` returns the string `"use strict"`.
+ */
+ (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
+ ) {
+ const statements = node.type === "Program" ? node.body : node.body.body;
+
+ for (const statement of statements) {
+ if (
+ statement.type === "ExpressionStatement" &&
+ statement.expression.type === "Literal"
+ ) {
+ directives.push(statement);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return directives;
+ },
+
+
+ /**
+ * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
+ * after the node will be parsed as a decimal point, rather than a property-access dot.
+ * @param {ASTNode} node - The node to check.
+ * @returns {boolean} `true` if this node is a decimal integer.
+ * @example
+ *
+ * 5 // true
+ * 5. // false
+ * 5.0 // false
+ * 05 // false
+ * 0x5 // false
+ * 0b101 // false
+ * 0o5 // false
+ * 5e0 // false
+ * '5' // false
+ */
+ isDecimalInteger(node) {
+ return node.type === "Literal" && typeof node.value === "number" && /^(0|[1-9]\d*)$/.test(node.raw);
+ },
+
+ /**
+ * Gets the name and kind of the given function node.
+ *
+ * - `function foo() {}` .................... `function 'foo'`
+ * - `(function foo() {})` .................. `function 'foo'`
+ * - `(function() {})` ...................... `function`
+ * - `function* foo() {}` ................... `generator function 'foo'`
+ * - `(function* foo() {})` ................. `generator function 'foo'`
+ * - `(function*() {})` ..................... `generator function`
+ * - `() => {}` ............................. `arrow function`
+ * - `async () => {}` ....................... `async arrow function`
+ * - `({ foo: function foo() {} })` ......... `method 'foo'`
+ * - `({ foo: function() {} })` ............. `method 'foo'`
+ * - `({ ['foo']: function() {} })` ......... `method 'foo'`
+ * - `({ [foo]: function() {} })` ........... `method`
+ * - `({ foo() {} })` ....................... `method 'foo'`
+ * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
+ * - `({ foo: function*() {} })` ............ `generator method 'foo'`
+ * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
+ * - `({ [foo]: function*() {} })` .......... `generator method`
+ * - `({ *foo() {} })` ...................... `generator method 'foo'`
+ * - `({ foo: async function foo() {} })` ... `async method 'foo'`
+ * - `({ foo: async function() {} })` ....... `async method 'foo'`
+ * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
+ * - `({ [foo]: async function() {} })` ..... `async method`
+ * - `({ async foo() {} })` ................. `async method 'foo'`
+ * - `({ get foo() {} })` ................... `getter 'foo'`
+ * - `({ set foo(a) {} })` .................. `setter 'foo'`
+ * - `class A { constructor() {} }` ......... `constructor`
+ * - `class A { foo() {} }` ................. `method 'foo'`
+ * - `class A { *foo() {} }` ................ `generator method 'foo'`
+ * - `class A { async foo() {} }` ........... `async method 'foo'`
+ * - `class A { ['foo']() {} }` ............. `method 'foo'`
+ * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
+ * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
+ * - `class A { [foo]() {} }` ............... `method`
+ * - `class A { *[foo]() {} }` .............. `generator method`
+ * - `class A { async [foo]() {} }` ......... `async method`
+ * - `class A { get foo() {} }` ............. `getter 'foo'`
+ * - `class A { set foo(a) {} }` ............ `setter 'foo'`
+ * - `class A { static foo() {} }` .......... `static method 'foo'`
+ * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
+ * - `class A { static async foo() {} }` .... `static async method 'foo'`
+ * - `class A { static get foo() {} }` ...... `static getter 'foo'`
+ * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
+ *
+ * @param {ASTNode} node - The function node to get.
+ * @returns {string} The name and kind of the function node.
+ */
+ getFunctionNameWithKind(node) {
+ const parent = node.parent;
+ const tokens = [];
+
+ if (parent.type === "MethodDefinition" && parent.static) {
+ tokens.push("static");
+ }
+ if (node.async) {
+ tokens.push("async");
+ }
+ if (node.generator) {
+ tokens.push("generator");
+ }
+
+ if (node.type === "ArrowFunctionExpression") {
+ tokens.push("arrow", "function");
+ } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
+ if (parent.kind === "constructor") {
+ return "constructor";
+ }
+ if (parent.kind === "get") {
+ tokens.push("getter");
+ } else if (parent.kind === "set") {
+ tokens.push("setter");
+ } else {
+ tokens.push("method");
+ }
+ } else {
+ tokens.push("function");
+ }
+
+ if (node.id) {
+ tokens.push(`'${node.id.name}'`);
+ } else {
+ const name = module.exports.getStaticPropertyName(parent);
+
+ if (name) {
+ tokens.push(`'${name}'`);
+ }
+ }
+
+ return tokens.join(" ");
+ },
+
+ /**
+ * Gets the location of the given function node for reporting.
+ *
+ * - `function foo() {}`
+ * ^^^^^^^^^^^^
+ * - `(function foo() {})`
+ * ^^^^^^^^^^^^
+ * - `(function() {})`
+ * ^^^^^^^^
+ * - `function* foo() {}`
+ * ^^^^^^^^^^^^^
+ * - `(function* foo() {})`
+ * ^^^^^^^^^^^^^
+ * - `(function*() {})`
+ * ^^^^^^^^^
+ * - `() => {}`
+ * ^^
+ * - `async () => {}`
+ * ^^
+ * - `({ foo: function foo() {} })`
+ * ^^^^^^^^^^^^^^^^^
+ * - `({ foo: function() {} })`
+ * ^^^^^^^^^^^^^
+ * - `({ ['foo']: function() {} })`
+ * ^^^^^^^^^^^^^^^^^
+ * - `({ [foo]: function() {} })`
+ * ^^^^^^^^^^^^^^^
+ * - `({ foo() {} })`
+ * ^^^
+ * - `({ foo: function* foo() {} })`
+ * ^^^^^^^^^^^^^^^^^^
+ * - `({ foo: function*() {} })`
+ * ^^^^^^^^^^^^^^
+ * - `({ ['foo']: function*() {} })`
+ * ^^^^^^^^^^^^^^^^^^
+ * - `({ [foo]: function*() {} })`
+ * ^^^^^^^^^^^^^^^^
+ * - `({ *foo() {} })`
+ * ^^^^
+ * - `({ foo: async function foo() {} })`
+ * ^^^^^^^^^^^^^^^^^^^^^^^
+ * - `({ foo: async function() {} })`
+ * ^^^^^^^^^^^^^^^^^^^
+ * - `({ ['foo']: async function() {} })`
+ * ^^^^^^^^^^^^^^^^^^^^^^^
+ * - `({ [foo]: async function() {} })`
+ * ^^^^^^^^^^^^^^^^^^^^^
+ * - `({ async foo() {} })`
+ * ^^^^^^^^^
+ * - `({ get foo() {} })`
+ * ^^^^^^^
+ * - `({ set foo(a) {} })`
+ * ^^^^^^^
+ * - `class A { constructor() {} }`
+ * ^^^^^^^^^^^
+ * - `class A { foo() {} }`
+ * ^^^
+ * - `class A { *foo() {} }`
+ * ^^^^
+ * - `class A { async foo() {} }`
+ * ^^^^^^^^^
+ * - `class A { ['foo']() {} }`
+ * ^^^^^^^
+ * - `class A { *['foo']() {} }`
+ * ^^^^^^^^
+ * - `class A { async ['foo']() {} }`
+ * ^^^^^^^^^^^^^
+ * - `class A { [foo]() {} }`
+ * ^^^^^
+ * - `class A { *[foo]() {} }`
+ * ^^^^^^
+ * - `class A { async [foo]() {} }`
+ * ^^^^^^^^^^^
+ * - `class A { get foo() {} }`
+ * ^^^^^^^
+ * - `class A { set foo(a) {} }`
+ * ^^^^^^^
+ * - `class A { static foo() {} }`
+ * ^^^^^^^^^^
+ * - `class A { static *foo() {} }`
+ * ^^^^^^^^^^^
+ * - `class A { static async foo() {} }`
+ * ^^^^^^^^^^^^^^^^
+ * - `class A { static get foo() {} }`
+ * ^^^^^^^^^^^^^^
+ * - `class A { static set foo(a) {} }`
+ * ^^^^^^^^^^^^^^
+ *
+ * @param {ASTNode} node - The function node to get.
+ * @param {SourceCode} sourceCode - The source code object to get tokens.
+ * @returns {string} The location of the function node for reporting.
+ */
+ getFunctionHeadLoc(node, sourceCode) {
+ const parent = node.parent;
+ let start = null;
+ let end = null;
+
+ if (node.type === "ArrowFunctionExpression") {
+ const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
+
+ start = arrowToken.loc.start;
+ end = arrowToken.loc.end;
+ } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
+ start = parent.loc.start;
+ end = getOpeningParenOfParams(node, sourceCode).loc.start;
+ } else {
+ start = node.loc.start;
+ end = getOpeningParenOfParams(node, sourceCode).loc.start;
+ }
+
+ return {
+ start: Object.assign({}, start),
+ end: Object.assign({}, end)
+ };
+ },
+
+ /**
+ * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
+ * surrounding the node.
+ * @param {SourceCode} sourceCode The source code object
+ * @param {ASTNode} node An expression node
+ * @returns {string} The text representing the node, with all surrounding parentheses included
+ */
+ getParenthesisedText(sourceCode, node) {
+ let leftToken = sourceCode.getFirstToken(node);
+ let rightToken = sourceCode.getLastToken(node);
+
+ while (
+ sourceCode.getTokenBefore(leftToken) &&
+ sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
+ sourceCode.getTokenBefore(leftToken).value === "(" &&
+ sourceCode.getTokenAfter(rightToken) &&
+ sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
+ sourceCode.getTokenAfter(rightToken).value === ")"
+ ) {
+ leftToken = sourceCode.getTokenBefore(leftToken);
+ rightToken = sourceCode.getTokenAfter(rightToken);
+ }
+
+ return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
+ },
+
+ /*
+ * Determine if a node has a possiblity to be an Error object
+ * @param {ASTNode} node ASTNode to check
+ * @returns {boolean} True if there is a chance it contains an Error obj
+ */
+ couldBeError(node) {
+ switch (node.type) {
+ case "Identifier":
+ case "CallExpression":
+ case "NewExpression":
+ case "MemberExpression":
+ case "TaggedTemplateExpression":
+ case "YieldExpression":
+ case "AwaitExpression":
+ return true; // possibly an error object.
+
+ case "AssignmentExpression":
+ return module.exports.couldBeError(node.right);
+
+ case "SequenceExpression": {
+ const exprs = node.expressions;
+
+ return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
+ }
+
+ case "LogicalExpression":
+ return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
+
+ case "ConditionalExpression":
+ return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
+
+ default:
+ return false;
+ }
+ },
+
+ /**
+ * Determines whether the given node is a `null` literal.
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} `true` if the node is a `null` literal
+ */
+ isNullLiteral(node) {
+
+ /*
+ * Checking `node.value === null` does not guarantee that a literal is a null literal.
+ * When parsing values that cannot be represented in the current environment (e.g. unicode
+ * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
+ * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
+ * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
+ */
+ return node.type === "Literal" && node.value === null && !node.regex;
+ },
+
+ /**
+ * Determines whether two tokens can safely be placed next to each other without merging into a single token
+ * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
+ * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
+ * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
+ * next to each other, behavior is undefined (although it should return `true` in most cases).
+ */
+ canTokensBeAdjacent(leftValue, rightValue) {
+ let leftToken;
+
+ if (typeof leftValue === "string") {
+ const leftTokens = espree.tokenize(leftValue, { ecmaVersion: 2015 });
+
+ leftToken = leftTokens[leftTokens.length - 1];
+ } else {
+ leftToken = leftValue;
+ }
+
+ const rightToken = typeof rightValue === "string" ? espree.tokenize(rightValue, { ecmaVersion: 2015 })[0] : rightValue;
+
+ if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
+ if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
+ const PLUS_TOKENS = new Set(["+", "++"]);
+ const MINUS_TOKENS = new Set(["-", "--"]);
+
+ return !(
+ PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
+ MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
+ );
+ }
+ return true;
+ }
+
+ if (
+ leftToken.type === "String" || rightToken.type === "String" ||
+ leftToken.type === "Template" || rightToken.type === "Template"
+ ) {
+ return true;
+ }
+
+ if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
+ return true;
+ }
+
+ return false;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/cli-engine.js b/tools/node_modules/eslint/lib/cli-engine.js
new file mode 100644
index 0000000000..55450fd633
--- /dev/null
+++ b/tools/node_modules/eslint/lib/cli-engine.js
@@ -0,0 +1,715 @@
+/**
+ * @fileoverview Main CLI object.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+/*
+ * The CLI object should *not* call process.exit() directly. It should only return
+ * exit codes. This allows other programs to use the CLI object and still control
+ * when the program exits.
+ */
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path"),
+ defaultOptions = require("../conf/default-cli-options"),
+ Linter = require("./linter"),
+ IgnoredPaths = require("./ignored-paths"),
+ Config = require("./config"),
+ fileEntryCache = require("file-entry-cache"),
+ globUtil = require("./util/glob-util"),
+ validator = require("./config/config-validator"),
+ stringify = require("json-stable-stringify-without-jsonify"),
+ hash = require("./util/hash"),
+ ModuleResolver = require("./util/module-resolver"),
+ naming = require("./util/naming"),
+ pkg = require("../package.json");
+
+const debug = require("debug")("eslint:cli-engine");
+
+const resolver = new ModuleResolver();
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * The options to configure a CLI engine with.
+ * @typedef {Object} CLIEngineOptions
+ * @property {boolean} allowInlineConfig Enable or disable inline configuration comments.
+ * @property {boolean|Object} baseConfig Base config object. True enables recommend rules and environments.
+ * @property {boolean} cache Enable result caching.
+ * @property {string} cacheLocation The cache file to use instead of .eslintcache.
+ * @property {string} configFile The configuration file to use.
+ * @property {string} cwd The value to use for the current working directory.
+ * @property {string[]} envs An array of environments to load.
+ * @property {string[]} extensions An array of file extensions to check.
+ * @property {boolean|Function} fix Execute in autofix mode. If a function, should return a boolean.
+ * @property {string[]} globals An array of global variables to declare.
+ * @property {boolean} ignore False disables use of .eslintignore.
+ * @property {string} ignorePath The ignore file to use instead of .eslintignore.
+ * @property {string} ignorePattern A glob pattern of files to ignore.
+ * @property {boolean} useEslintrc False disables looking for .eslintrc
+ * @property {string} parser The name of the parser to use.
+ * @property {Object} parserOptions An object of parserOption settings to use.
+ * @property {string[]} plugins An array of plugins to load.
+ * @property {Object<string,*>} rules An object of rules to use.
+ * @property {string[]} rulePaths An array of directories to load custom rules from.
+ * @property {boolean} reportUnusedDisableDirectives `true` adds reports for unused eslint-disable directives
+ */
+
+/**
+ * A linting warning or error.
+ * @typedef {Object} LintMessage
+ * @property {string} message The message to display to the user.
+ */
+
+/**
+ * A linting result.
+ * @typedef {Object} LintResult
+ * @property {string} filePath The path to the file that was linted.
+ * @property {LintMessage[]} messages All of the messages for the result.
+ * @property {number} errorCount Number of errors for the result.
+ * @property {number} warningCount Number of warnings for the result.
+ * @property {number} fixableErrorCount Number of fixable errors for the result.
+ * @property {number} fixableWarningCount Number of fixable warnings for the result.
+ * @property {string=} [source] The source code of the file that was linted.
+ * @property {string=} [output] The source code of the file that was linted, with as many fixes applied as possible.
+ */
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * It will calculate the error and warning count for collection of messages per file
+ * @param {Object[]} messages - Collection of messages
+ * @returns {Object} Contains the stats
+ * @private
+ */
+function calculateStatsPerFile(messages) {
+ return messages.reduce((stat, message) => {
+ if (message.fatal || message.severity === 2) {
+ stat.errorCount++;
+ if (message.fix) {
+ stat.fixableErrorCount++;
+ }
+ } else {
+ stat.warningCount++;
+ if (message.fix) {
+ stat.fixableWarningCount++;
+ }
+ }
+ return stat;
+ }, {
+ errorCount: 0,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0
+ });
+}
+
+/**
+ * It will calculate the error and warning count for collection of results from all files
+ * @param {Object[]} results - Collection of messages from all the files
+ * @returns {Object} Contains the stats
+ * @private
+ */
+function calculateStatsPerRun(results) {
+ return results.reduce((stat, result) => {
+ stat.errorCount += result.errorCount;
+ stat.warningCount += result.warningCount;
+ stat.fixableErrorCount += result.fixableErrorCount;
+ stat.fixableWarningCount += result.fixableWarningCount;
+ return stat;
+ }, {
+ errorCount: 0,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0
+ });
+}
+
+/**
+ * Processes an source code using ESLint.
+ * @param {string} text The source code to check.
+ * @param {Object} configHelper The configuration options for ESLint.
+ * @param {string} filename An optional string representing the texts filename.
+ * @param {boolean|Function} fix Indicates if fixes should be processed.
+ * @param {boolean} allowInlineConfig Allow/ignore comments that change config.
+ * @param {boolean} reportUnusedDisableDirectives Allow/ignore comments that change config.
+ * @param {Linter} linter Linter context
+ * @returns {LintResult} The results for linting on this text.
+ * @private
+ */
+function processText(text, configHelper, filename, fix, allowInlineConfig, reportUnusedDisableDirectives, linter) {
+ let filePath,
+ fileExtension,
+ processor;
+
+ if (filename) {
+ filePath = path.resolve(filename);
+ fileExtension = path.extname(filename);
+ }
+
+ filename = filename || "<text>";
+ debug(`Linting ${filename}`);
+ const config = configHelper.getConfig(filePath);
+
+ if (config.plugins) {
+ configHelper.plugins.loadAll(config.plugins);
+ }
+
+ const loadedPlugins = configHelper.plugins.getAll();
+
+ for (const plugin in loadedPlugins) {
+ if (loadedPlugins[plugin].processors && Object.keys(loadedPlugins[plugin].processors).indexOf(fileExtension) >= 0) {
+ processor = loadedPlugins[plugin].processors[fileExtension];
+ break;
+ }
+ }
+
+ const autofixingEnabled = typeof fix !== "undefined" && (!processor || processor.supportsAutofix);
+
+ const fixedResult = linter.verifyAndFix(text, config, {
+ filename,
+ allowInlineConfig,
+ reportUnusedDisableDirectives,
+ fix: !!autofixingEnabled && fix,
+ preprocess: processor && (rawText => processor.preprocess(rawText, filename)),
+ postprocess: processor && (problemLists => processor.postprocess(problemLists, filename))
+ });
+
+ const stats = calculateStatsPerFile(fixedResult.messages);
+
+ const result = {
+ filePath: filename,
+ messages: fixedResult.messages,
+ errorCount: stats.errorCount,
+ warningCount: stats.warningCount,
+ fixableErrorCount: stats.fixableErrorCount,
+ fixableWarningCount: stats.fixableWarningCount
+ };
+
+ if (fixedResult.fixed) {
+ result.output = fixedResult.output;
+ }
+
+ if (result.errorCount + result.warningCount > 0 && typeof result.output === "undefined") {
+ result.source = text;
+ }
+
+ return result;
+}
+
+/**
+ * Processes an individual file using ESLint. Files used here are known to
+ * exist, so no need to check that here.
+ * @param {string} filename The filename of the file being checked.
+ * @param {Object} configHelper The configuration options for ESLint.
+ * @param {Object} options The CLIEngine options object.
+ * @param {Linter} linter Linter context
+ * @returns {LintResult} The results for linting on this file.
+ * @private
+ */
+function processFile(filename, configHelper, options, linter) {
+
+ const text = fs.readFileSync(path.resolve(filename), "utf8"),
+ result = processText(
+ text,
+ configHelper,
+ filename,
+ options.fix,
+ options.allowInlineConfig,
+ options.reportUnusedDisableDirectives,
+ linter
+ );
+
+ return result;
+
+}
+
+/**
+ * Returns result with warning by ignore settings
+ * @param {string} filePath - File path of checked code
+ * @param {string} baseDir - Absolute path of base directory
+ * @returns {LintResult} Result with single warning
+ * @private
+ */
+function createIgnoreResult(filePath, baseDir) {
+ let message;
+ const isHidden = /^\./.test(path.basename(filePath));
+ const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
+ const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
+
+ if (isHidden) {
+ message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
+ } else if (isInNodeModules) {
+ message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
+ } else if (isInBowerComponents) {
+ message = "File ignored by default. Use \"--ignore-pattern '!bower_components/*'\" to override.";
+ } else {
+ message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
+ }
+
+ return {
+ filePath: path.resolve(filePath),
+ messages: [
+ {
+ fatal: false,
+ severity: 1,
+ message
+ }
+ ],
+ errorCount: 0,
+ warningCount: 1,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0
+ };
+}
+
+
+/**
+ * Checks if the given message is an error message.
+ * @param {Object} message The message to check.
+ * @returns {boolean} Whether or not the message is an error message.
+ * @private
+ */
+function isErrorMessage(message) {
+ return message.severity === 2;
+}
+
+
+/**
+ * return the cacheFile to be used by eslint, based on whether the provided parameter is
+ * a directory or looks like a directory (ends in `path.sep`), in which case the file
+ * name will be the `cacheFile/.cache_hashOfCWD`
+ *
+ * if cacheFile points to a file or looks like a file then in will just use that file
+ *
+ * @param {string} cacheFile The name of file to be used to store the cache
+ * @param {string} cwd Current working directory
+ * @returns {string} the resolved path to the cache file
+ */
+function getCacheFile(cacheFile, cwd) {
+
+ /*
+ * make sure the path separators are normalized for the environment/os
+ * keeping the trailing path separator if present
+ */
+ cacheFile = path.normalize(cacheFile);
+
+ const resolvedCacheFile = path.resolve(cwd, cacheFile);
+ const looksLikeADirectory = cacheFile[cacheFile.length - 1] === path.sep;
+
+ /**
+ * return the name for the cache file in case the provided parameter is a directory
+ * @returns {string} the resolved path to the cacheFile
+ */
+ function getCacheFileForDirectory() {
+ return path.join(resolvedCacheFile, `.cache_${hash(cwd)}`);
+ }
+
+ let fileStats;
+
+ try {
+ fileStats = fs.lstatSync(resolvedCacheFile);
+ } catch (ex) {
+ fileStats = null;
+ }
+
+
+ /*
+ * in case the file exists we need to verify if the provided path
+ * is a directory or a file. If it is a directory we want to create a file
+ * inside that directory
+ */
+ if (fileStats) {
+
+ /*
+ * is a directory or is a file, but the original file the user provided
+ * looks like a directory but `path.resolve` removed the `last path.sep`
+ * so we need to still treat this like a directory
+ */
+ if (fileStats.isDirectory() || looksLikeADirectory) {
+ return getCacheFileForDirectory();
+ }
+
+ // is file so just use that file
+ return resolvedCacheFile;
+ }
+
+ /*
+ * here we known the file or directory doesn't exist,
+ * so we will try to infer if its a directory if it looks like a directory
+ * for the current operating system.
+ */
+
+ // if the last character passed is a path separator we assume is a directory
+ if (looksLikeADirectory) {
+ return getCacheFileForDirectory();
+ }
+
+ return resolvedCacheFile;
+}
+
+const configHashCache = new WeakMap();
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+class CLIEngine {
+
+ /**
+ * Creates a new instance of the core CLI engine.
+ * @param {CLIEngineOptions} options The options for this instance.
+ * @constructor
+ */
+ constructor(options) {
+
+ options = Object.assign(
+ Object.create(null),
+ defaultOptions,
+ { cwd: process.cwd() },
+ options
+ );
+
+ /**
+ * Stored options for this instance
+ * @type {Object}
+ */
+ this.options = options;
+ this.linter = new Linter();
+
+ if (options.cache) {
+ const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
+
+ /**
+ * Cache used to avoid operating on files that haven't changed since the
+ * last successful execution (e.g., file passed linting with no errors and
+ * no warnings).
+ * @type {Object}
+ */
+ this._fileCache = fileEntryCache.create(cacheFile);
+ }
+
+ // load in additional rules
+ if (this.options.rulePaths) {
+ const cwd = this.options.cwd;
+
+ this.options.rulePaths.forEach(rulesdir => {
+ debug(`Loading rules from ${rulesdir}`);
+ this.linter.rules.load(rulesdir, cwd);
+ });
+ }
+
+ if (this.options.rules && Object.keys(this.options.rules).length) {
+ const loadedRules = this.linter.getRules();
+
+ Object.keys(this.options.rules).forEach(name => {
+ validator.validateRuleOptions(loadedRules.get(name), name, this.options.rules[name], "CLI");
+ });
+ }
+
+ this.config = new Config(this.options, this.linter);
+ }
+
+ /**
+ * Returns results that only contains errors.
+ * @param {LintResult[]} results The results to filter.
+ * @returns {LintResult[]} The filtered results.
+ */
+ static getErrorResults(results) {
+ const filtered = [];
+
+ results.forEach(result => {
+ const filteredMessages = result.messages.filter(isErrorMessage);
+
+ if (filteredMessages.length > 0) {
+ filtered.push(
+ Object.assign(result, {
+ messages: filteredMessages,
+ errorCount: filteredMessages.length,
+ warningCount: 0,
+ fixableErrorCount: result.fixableErrorCount,
+ fixableWarningCount: 0
+ })
+ );
+ }
+ });
+
+ return filtered;
+ }
+
+ /**
+ * Outputs fixes from the given results to files.
+ * @param {Object} report The report object created by CLIEngine.
+ * @returns {void}
+ */
+ static outputFixes(report) {
+ report.results.filter(result => result.hasOwnProperty("output")).forEach(result => {
+ fs.writeFileSync(result.filePath, result.output);
+ });
+ }
+
+
+ /**
+ * Add a plugin by passing its configuration
+ * @param {string} name Name of the plugin.
+ * @param {Object} pluginobject Plugin configuration object.
+ * @returns {void}
+ */
+ addPlugin(name, pluginobject) {
+ this.config.plugins.define(name, pluginobject);
+ }
+
+ /**
+ * Resolves the patterns passed into executeOnFiles() into glob-based patterns
+ * for easier handling.
+ * @param {string[]} patterns The file patterns passed on the command line.
+ * @returns {string[]} The equivalent glob patterns.
+ */
+ resolveFileGlobPatterns(patterns) {
+ return globUtil.resolveFileGlobPatterns(patterns, this.options);
+ }
+
+ /**
+ * Executes the current configuration on an array of file and directory names.
+ * @param {string[]} patterns An array of file and directory names.
+ * @returns {Object} The results for all files that were linted.
+ */
+ executeOnFiles(patterns) {
+ const options = this.options,
+ fileCache = this._fileCache,
+ configHelper = this.config;
+ const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
+
+ if (!options.cache && fs.existsSync(cacheFile)) {
+ fs.unlinkSync(cacheFile);
+ }
+
+ /**
+ * Calculates the hash of the config file used to validate a given file
+ * @param {string} filename The path of the file to retrieve a config object for to calculate the hash
+ * @returns {string} the hash of the config
+ */
+ function hashOfConfigFor(filename) {
+ const config = configHelper.getConfig(filename);
+
+ if (!configHashCache.has(config)) {
+ configHashCache.set(config, hash(`${pkg.version}_${stringify(config)}`));
+ }
+
+ return configHashCache.get(config);
+ }
+
+ const startTime = Date.now();
+ const fileList = globUtil.listFilesToProcess(this.resolveFileGlobPatterns(patterns), options);
+ const results = fileList.map(fileInfo => {
+ if (fileInfo.ignored) {
+ return createIgnoreResult(fileInfo.filename, options.cwd);
+ }
+
+ if (options.cache) {
+
+ /*
+ * get the descriptor for this file
+ * with the metadata and the flag that determines if
+ * the file has changed
+ */
+ const descriptor = fileCache.getFileDescriptor(fileInfo.filename);
+ const hashOfConfig = hashOfConfigFor(fileInfo.filename);
+ const changed = descriptor.changed || descriptor.meta.hashOfConfig !== hashOfConfig;
+
+ if (!changed) {
+ debug(`Skipping file since hasn't changed: ${fileInfo.filename}`);
+
+ /*
+ * Add the the cached results (always will be 0 error and
+ * 0 warnings). We should not cache results for files that
+ * failed, in order to guarantee that next execution will
+ * process those files as well.
+ */
+ return descriptor.meta.results;
+ }
+ }
+
+ debug(`Processing ${fileInfo.filename}`);
+
+ return processFile(fileInfo.filename, configHelper, options, this.linter);
+ });
+
+ if (options.cache) {
+ results.forEach(result => {
+ if (result.messages.length) {
+
+ /*
+ * if a file contains errors or warnings we don't want to
+ * store the file in the cache so we can guarantee that
+ * next execution will also operate on this file
+ */
+ fileCache.removeEntry(result.filePath);
+ } else {
+
+ /*
+ * since the file passed we store the result here
+ * TODO: it might not be necessary to store the results list in the cache,
+ * since it should always be 0 errors/warnings
+ */
+ const descriptor = fileCache.getFileDescriptor(result.filePath);
+
+ descriptor.meta.hashOfConfig = hashOfConfigFor(result.filePath);
+ descriptor.meta.results = result;
+ }
+ });
+
+ // persist the cache to disk
+ fileCache.reconcile();
+ }
+
+ const stats = calculateStatsPerRun(results);
+
+ debug(`Linting complete in: ${Date.now() - startTime}ms`);
+
+ return {
+ results,
+ errorCount: stats.errorCount,
+ warningCount: stats.warningCount,
+ fixableErrorCount: stats.fixableErrorCount,
+ fixableWarningCount: stats.fixableWarningCount
+ };
+ }
+
+ /**
+ * Executes the current configuration on text.
+ * @param {string} text A string of JavaScript code to lint.
+ * @param {string} filename An optional string representing the texts filename.
+ * @param {boolean} warnIgnored Always warn when a file is ignored
+ * @returns {Object} The results for the linting.
+ */
+ executeOnText(text, filename, warnIgnored) {
+
+ const results = [],
+ options = this.options,
+ configHelper = this.config,
+ ignoredPaths = new IgnoredPaths(options);
+
+ // resolve filename based on options.cwd (for reporting, ignoredPaths also resolves)
+ if (filename && !path.isAbsolute(filename)) {
+ filename = path.resolve(options.cwd, filename);
+ }
+
+ if (filename && ignoredPaths.contains(filename)) {
+ if (warnIgnored) {
+ results.push(createIgnoreResult(filename, options.cwd));
+ }
+ } else {
+ results.push(
+ processText(
+ text,
+ configHelper,
+ filename,
+ options.fix,
+ options.allowInlineConfig,
+ options.reportUnusedDisableDirectives,
+ this.linter
+ )
+ );
+ }
+
+ const stats = calculateStatsPerRun(results);
+
+ return {
+ results,
+ errorCount: stats.errorCount,
+ warningCount: stats.warningCount,
+ fixableErrorCount: stats.fixableErrorCount,
+ fixableWarningCount: stats.fixableWarningCount
+ };
+ }
+
+ /**
+ * Returns a configuration object for the given file based on the CLI options.
+ * This is the same logic used by the ESLint CLI executable to determine
+ * configuration for each file it processes.
+ * @param {string} filePath The path of the file to retrieve a config object for.
+ * @returns {Object} A configuration object for the file.
+ */
+ getConfigForFile(filePath) {
+ const configHelper = this.config;
+
+ return configHelper.getConfig(filePath);
+ }
+
+ /**
+ * Checks if a given path is ignored by ESLint.
+ * @param {string} filePath The path of the file to check.
+ * @returns {boolean} Whether or not the given path is ignored.
+ */
+ isPathIgnored(filePath) {
+ const resolvedPath = path.resolve(this.options.cwd, filePath);
+ const ignoredPaths = new IgnoredPaths(this.options);
+
+ return ignoredPaths.contains(resolvedPath);
+ }
+
+ /**
+ * Returns the formatter representing the given format or null if no formatter
+ * with the given name can be found.
+ * @param {string} [format] The name of the format to load or the path to a
+ * custom formatter.
+ * @returns {Function} The formatter function or null if not found.
+ */
+ getFormatter(format) {
+
+
+ // default is stylish
+ format = format || "stylish";
+
+ // only strings are valid formatters
+ if (typeof format === "string") {
+
+ // replace \ with / for Windows compatibility
+ format = format.replace(/\\/g, "/");
+
+ const cwd = this.options ? this.options.cwd : process.cwd();
+ const namespace = naming.getNamespaceFromTerm(format);
+
+ let formatterPath;
+
+ // if there's a slash, then it's a file
+ if (!namespace && format.indexOf("/") > -1) {
+ formatterPath = path.resolve(cwd, format);
+ } else {
+ try {
+ const npmFormat = naming.normalizePackageName(format, "eslint-formatter");
+
+ formatterPath = resolver.resolve(npmFormat, `${cwd}/node_modules`);
+ } catch (e) {
+ formatterPath = `./formatters/${format}`;
+ }
+ }
+
+ try {
+ return require(formatterPath);
+ } catch (ex) {
+ ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
+ throw ex;
+ }
+
+ } else {
+ return null;
+ }
+ }
+}
+
+CLIEngine.version = pkg.version;
+CLIEngine.getFormatter = CLIEngine.prototype.getFormatter;
+
+module.exports = CLIEngine;
diff --git a/tools/node_modules/eslint/lib/cli.js b/tools/node_modules/eslint/lib/cli.js
new file mode 100644
index 0000000000..6a5482bf9a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/cli.js
@@ -0,0 +1,219 @@
+/**
+ * @fileoverview Main CLI object.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+/*
+ * The CLI object should *not* call process.exit() directly. It should only return
+ * exit codes. This allows other programs to use the CLI object and still control
+ * when the program exits.
+ */
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path"),
+ options = require("./options"),
+ CLIEngine = require("./cli-engine"),
+ mkdirp = require("mkdirp"),
+ log = require("./logging");
+
+const debug = require("debug")("eslint:cli");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Predicate function for whether or not to apply fixes in quiet mode.
+ * If a message is a warning, do not apply a fix.
+ * @param {LintResult} lintResult The lint result.
+ * @returns {boolean} True if the lint message is an error (and thus should be
+ * autofixed), false otherwise.
+ */
+function quietFixPredicate(lintResult) {
+ return lintResult.severity === 2;
+}
+
+/**
+ * Translates the CLI options into the options expected by the CLIEngine.
+ * @param {Object} cliOptions The CLI options to translate.
+ * @returns {CLIEngineOptions} The options object for the CLIEngine.
+ * @private
+ */
+function translateOptions(cliOptions) {
+ return {
+ envs: cliOptions.env,
+ extensions: cliOptions.ext,
+ rules: cliOptions.rule,
+ plugins: cliOptions.plugin,
+ globals: cliOptions.global,
+ ignore: cliOptions.ignore,
+ ignorePath: cliOptions.ignorePath,
+ ignorePattern: cliOptions.ignorePattern,
+ configFile: cliOptions.config,
+ rulePaths: cliOptions.rulesdir,
+ useEslintrc: cliOptions.eslintrc,
+ parser: cliOptions.parser,
+ parserOptions: cliOptions.parserOptions,
+ cache: cliOptions.cache,
+ cacheFile: cliOptions.cacheFile,
+ cacheLocation: cliOptions.cacheLocation,
+ fix: (cliOptions.fix || cliOptions.fixDryRun) && (cliOptions.quiet ? quietFixPredicate : true),
+ allowInlineConfig: cliOptions.inlineConfig,
+ reportUnusedDisableDirectives: cliOptions.reportUnusedDisableDirectives
+ };
+}
+
+/**
+ * Outputs the results of the linting.
+ * @param {CLIEngine} engine The CLIEngine to use.
+ * @param {LintResult[]} results The results to print.
+ * @param {string} format The name of the formatter to use or the path to the formatter.
+ * @param {string} outputFile The path for the output file.
+ * @returns {boolean} True if the printing succeeds, false if not.
+ * @private
+ */
+function printResults(engine, results, format, outputFile) {
+ let formatter;
+
+ try {
+ formatter = engine.getFormatter(format);
+ } catch (e) {
+ log.error(e.message);
+ return false;
+ }
+
+ const output = formatter(results);
+
+ if (output) {
+ if (outputFile) {
+ const filePath = path.resolve(process.cwd(), outputFile);
+
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
+ log.error("Cannot write to output file path, it is a directory: %s", outputFile);
+ return false;
+ }
+
+ try {
+ mkdirp.sync(path.dirname(filePath));
+ fs.writeFileSync(filePath, output);
+ } catch (ex) {
+ log.error("There was a problem writing the output file:\n%s", ex);
+ return false;
+ }
+ } else {
+ log.info(output);
+ }
+ }
+
+ return true;
+
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Encapsulates all CLI behavior for eslint. Makes it easier to test as well as
+ * for other Node.js programs to effectively run the CLI.
+ */
+const cli = {
+
+ /**
+ * Executes the CLI based on an array of arguments that is passed in.
+ * @param {string|Array|Object} args The arguments to process.
+ * @param {string} [text] The text to lint (used for TTY).
+ * @returns {int} The exit code for the operation.
+ */
+ execute(args, text) {
+
+ let currentOptions;
+
+ try {
+ currentOptions = options.parse(args);
+ } catch (error) {
+ log.error(error.message);
+ return 1;
+ }
+
+ const files = currentOptions._;
+
+ const useStdin = typeof text === "string";
+
+ if (currentOptions.version) { // version from package.json
+
+ log.info(`v${require("../package.json").version}`);
+
+ } else if (currentOptions.printConfig) {
+ if (files.length) {
+ log.error("The --print-config option must be used with exactly one file name.");
+ return 1;
+ }
+ if (useStdin) {
+ log.error("The --print-config option is not available for piped-in code.");
+ return 1;
+ }
+
+ const engine = new CLIEngine(translateOptions(currentOptions));
+
+ const fileConfig = engine.getConfigForFile(currentOptions.printConfig);
+
+ log.info(JSON.stringify(fileConfig, null, " "));
+ return 0;
+ } else if (currentOptions.help || (!files.length && !useStdin)) {
+
+ log.info(options.generateHelp());
+
+ } else {
+
+ debug(`Running on ${useStdin ? "text" : "files"}`);
+
+ if (currentOptions.fix && currentOptions.fixDryRun) {
+ log.error("The --fix option and the --fix-dry-run option cannot be used together.");
+ return 1;
+ }
+
+ if (useStdin && currentOptions.fix) {
+ log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead.");
+ return 1;
+ }
+
+ const engine = new CLIEngine(translateOptions(currentOptions));
+
+ const report = useStdin ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files);
+
+ if (currentOptions.fix) {
+ debug("Fix mode enabled - applying fixes");
+ CLIEngine.outputFixes(report);
+ }
+
+ if (currentOptions.quiet) {
+ debug("Quiet mode enabled - filtering out warnings");
+ report.results = CLIEngine.getErrorResults(report.results);
+ }
+
+ if (printResults(engine, report.results, currentOptions.format, currentOptions.outputFile)) {
+ const tooManyWarnings = currentOptions.maxWarnings >= 0 && report.warningCount > currentOptions.maxWarnings;
+
+ if (!report.errorCount && tooManyWarnings) {
+ log.error("ESLint found too many warnings (maximum: %s).", currentOptions.maxWarnings);
+ }
+
+ return (report.errorCount || tooManyWarnings) ? 1 : 0;
+ }
+ return 1;
+
+
+ }
+
+ return 0;
+ }
+};
+
+module.exports = cli;
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js b/tools/node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js
new file mode 100644
index 0000000000..1a4f7870ba
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js
@@ -0,0 +1,659 @@
+/**
+ * @fileoverview A class of the code path analyzer.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("assert"),
+ CodePath = require("./code-path"),
+ CodePathSegment = require("./code-path-segment"),
+ IdGenerator = require("./id-generator"),
+ debug = require("./debug-helpers"),
+ astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is a `case` node (not `default` node).
+ *
+ * @param {ASTNode} node - A `SwitchCase` node to check.
+ * @returns {boolean} `true` if the node is a `case` node (not `default` node).
+ */
+function isCaseNode(node) {
+ return Boolean(node.test);
+}
+
+/**
+ * Checks whether or not a given logical expression node goes different path
+ * between the `true` case and the `false` case.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a test of a choice statement.
+ */
+function isForkingByTrueOrFalse(node) {
+ const parent = node.parent;
+
+ switch (parent.type) {
+ case "ConditionalExpression":
+ case "IfStatement":
+ case "WhileStatement":
+ case "DoWhileStatement":
+ case "ForStatement":
+ return parent.test === node;
+
+ case "LogicalExpression":
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Gets the boolean value of a given literal node.
+ *
+ * This is used to detect infinity loops (e.g. `while (true) {}`).
+ * Statements preceded by an infinity loop are unreachable if the loop didn't
+ * have any `break` statement.
+ *
+ * @param {ASTNode} node - A node to get.
+ * @returns {boolean|undefined} a boolean value if the node is a Literal node,
+ * otherwise `undefined`.
+ */
+function getBooleanValueIfSimpleConstant(node) {
+ if (node.type === "Literal") {
+ return Boolean(node.value);
+ }
+ return void 0;
+}
+
+/**
+ * Checks that a given identifier node is a reference or not.
+ *
+ * This is used to detect the first throwable node in a `try` block.
+ *
+ * @param {ASTNode} node - An Identifier node to check.
+ * @returns {boolean} `true` if the node is a reference.
+ */
+function isIdentifierReference(node) {
+ const parent = node.parent;
+
+ switch (parent.type) {
+ case "LabeledStatement":
+ case "BreakStatement":
+ case "ContinueStatement":
+ case "ArrayPattern":
+ case "RestElement":
+ case "ImportSpecifier":
+ case "ImportDefaultSpecifier":
+ case "ImportNamespaceSpecifier":
+ case "CatchClause":
+ return false;
+
+ case "FunctionDeclaration":
+ case "FunctionExpression":
+ case "ArrowFunctionExpression":
+ case "ClassDeclaration":
+ case "ClassExpression":
+ case "VariableDeclarator":
+ return parent.id !== node;
+
+ case "Property":
+ case "MethodDefinition":
+ return (
+ parent.key !== node ||
+ parent.computed ||
+ parent.shorthand
+ );
+
+ case "AssignmentPattern":
+ return parent.key !== node;
+
+ default:
+ return true;
+ }
+}
+
+/**
+ * Updates the current segment with the head segment.
+ * This is similar to local branches and tracking branches of git.
+ *
+ * To separate the current and the head is in order to not make useless segments.
+ *
+ * In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
+ * events are fired.
+ *
+ * @param {CodePathAnalyzer} analyzer - The instance.
+ * @param {ASTNode} node - The current AST node.
+ * @returns {void}
+ */
+function forwardCurrentToHead(analyzer, node) {
+ const codePath = analyzer.codePath;
+ const state = CodePath.getState(codePath);
+ const currentSegments = state.currentSegments;
+ const headSegments = state.headSegments;
+ const end = Math.max(currentSegments.length, headSegments.length);
+ let i, currentSegment, headSegment;
+
+ // Fires leaving events.
+ for (i = 0; i < end; ++i) {
+ currentSegment = currentSegments[i];
+ headSegment = headSegments[i];
+
+ if (currentSegment !== headSegment && currentSegment) {
+ debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
+
+ if (currentSegment.reachable) {
+ analyzer.emitter.emit(
+ "onCodePathSegmentEnd",
+ currentSegment,
+ node
+ );
+ }
+ }
+ }
+
+ // Update state.
+ state.currentSegments = headSegments;
+
+ // Fires entering events.
+ for (i = 0; i < end; ++i) {
+ currentSegment = currentSegments[i];
+ headSegment = headSegments[i];
+
+ if (currentSegment !== headSegment && headSegment) {
+ debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
+
+ CodePathSegment.markUsed(headSegment);
+ if (headSegment.reachable) {
+ analyzer.emitter.emit(
+ "onCodePathSegmentStart",
+ headSegment,
+ node
+ );
+ }
+ }
+ }
+
+}
+
+/**
+ * Updates the current segment with empty.
+ * This is called at the last of functions or the program.
+ *
+ * @param {CodePathAnalyzer} analyzer - The instance.
+ * @param {ASTNode} node - The current AST node.
+ * @returns {void}
+ */
+function leaveFromCurrentSegment(analyzer, node) {
+ const state = CodePath.getState(analyzer.codePath);
+ const currentSegments = state.currentSegments;
+
+ for (let i = 0; i < currentSegments.length; ++i) {
+ const currentSegment = currentSegments[i];
+
+ debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
+ if (currentSegment.reachable) {
+ analyzer.emitter.emit(
+ "onCodePathSegmentEnd",
+ currentSegment,
+ node
+ );
+ }
+ }
+
+ state.currentSegments = [];
+}
+
+/**
+ * Updates the code path due to the position of a given node in the parent node
+ * thereof.
+ *
+ * For example, if the node is `parent.consequent`, this creates a fork from the
+ * current path.
+ *
+ * @param {CodePathAnalyzer} analyzer - The instance.
+ * @param {ASTNode} node - The current AST node.
+ * @returns {void}
+ */
+function preprocess(analyzer, node) {
+ const codePath = analyzer.codePath;
+ const state = CodePath.getState(codePath);
+ const parent = node.parent;
+
+ switch (parent.type) {
+ case "LogicalExpression":
+ if (parent.right === node) {
+ state.makeLogicalRight();
+ }
+ break;
+
+ case "ConditionalExpression":
+ case "IfStatement":
+
+ /*
+ * Fork if this node is at `consequent`/`alternate`.
+ * `popForkContext()` exists at `IfStatement:exit` and
+ * `ConditionalExpression:exit`.
+ */
+ if (parent.consequent === node) {
+ state.makeIfConsequent();
+ } else if (parent.alternate === node) {
+ state.makeIfAlternate();
+ }
+ break;
+
+ case "SwitchCase":
+ if (parent.consequent[0] === node) {
+ state.makeSwitchCaseBody(false, !parent.test);
+ }
+ break;
+
+ case "TryStatement":
+ if (parent.handler === node) {
+ state.makeCatchBlock();
+ } else if (parent.finalizer === node) {
+ state.makeFinallyBlock();
+ }
+ break;
+
+ case "WhileStatement":
+ if (parent.test === node) {
+ state.makeWhileTest(getBooleanValueIfSimpleConstant(node));
+ } else {
+ assert(parent.body === node);
+ state.makeWhileBody();
+ }
+ break;
+
+ case "DoWhileStatement":
+ if (parent.body === node) {
+ state.makeDoWhileBody();
+ } else {
+ assert(parent.test === node);
+ state.makeDoWhileTest(getBooleanValueIfSimpleConstant(node));
+ }
+ break;
+
+ case "ForStatement":
+ if (parent.test === node) {
+ state.makeForTest(getBooleanValueIfSimpleConstant(node));
+ } else if (parent.update === node) {
+ state.makeForUpdate();
+ } else if (parent.body === node) {
+ state.makeForBody();
+ }
+ break;
+
+ case "ForInStatement":
+ case "ForOfStatement":
+ if (parent.left === node) {
+ state.makeForInOfLeft();
+ } else if (parent.right === node) {
+ state.makeForInOfRight();
+ } else {
+ assert(parent.body === node);
+ state.makeForInOfBody();
+ }
+ break;
+
+ case "AssignmentPattern":
+
+ /*
+ * Fork if this node is at `right`.
+ * `left` is executed always, so it uses the current path.
+ * `popForkContext()` exists at `AssignmentPattern:exit`.
+ */
+ if (parent.right === node) {
+ state.pushForkContext();
+ state.forkBypassPath();
+ state.forkPath();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * Updates the code path due to the type of a given node in entering.
+ *
+ * @param {CodePathAnalyzer} analyzer - The instance.
+ * @param {ASTNode} node - The current AST node.
+ * @returns {void}
+ */
+function processCodePathToEnter(analyzer, node) {
+ let codePath = analyzer.codePath;
+ let state = codePath && CodePath.getState(codePath);
+ const parent = node.parent;
+
+ switch (node.type) {
+ case "Program":
+ case "FunctionDeclaration":
+ case "FunctionExpression":
+ case "ArrowFunctionExpression":
+ if (codePath) {
+
+ // Emits onCodePathSegmentStart events if updated.
+ forwardCurrentToHead(analyzer, node);
+ debug.dumpState(node, state, false);
+ }
+
+ // Create the code path of this scope.
+ codePath = analyzer.codePath = new CodePath(
+ analyzer.idGenerator.next(),
+ codePath,
+ analyzer.onLooped
+ );
+ state = CodePath.getState(codePath);
+
+ // Emits onCodePathStart events.
+ debug.dump(`onCodePathStart ${codePath.id}`);
+ analyzer.emitter.emit("onCodePathStart", codePath, node);
+ break;
+
+ case "LogicalExpression":
+ state.pushChoiceContext(node.operator, isForkingByTrueOrFalse(node));
+ break;
+
+ case "ConditionalExpression":
+ case "IfStatement":
+ state.pushChoiceContext("test", false);
+ break;
+
+ case "SwitchStatement":
+ state.pushSwitchContext(
+ node.cases.some(isCaseNode),
+ astUtils.getLabel(node)
+ );
+ break;
+
+ case "TryStatement":
+ state.pushTryContext(Boolean(node.finalizer));
+ break;
+
+ case "SwitchCase":
+
+ /*
+ * Fork if this node is after the 2st node in `cases`.
+ * It's similar to `else` blocks.
+ * The next `test` node is processed in this path.
+ */
+ if (parent.discriminant !== node && parent.cases[0] !== node) {
+ state.forkPath();
+ }
+ break;
+
+ case "WhileStatement":
+ case "DoWhileStatement":
+ case "ForStatement":
+ case "ForInStatement":
+ case "ForOfStatement":
+ state.pushLoopContext(node.type, astUtils.getLabel(node));
+ break;
+
+ case "LabeledStatement":
+ if (!astUtils.isBreakableStatement(node.body)) {
+ state.pushBreakContext(false, node.label.name);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Emits onCodePathSegmentStart events if updated.
+ forwardCurrentToHead(analyzer, node);
+ debug.dumpState(node, state, false);
+}
+
+/**
+ * Updates the code path due to the type of a given node in leaving.
+ *
+ * @param {CodePathAnalyzer} analyzer - The instance.
+ * @param {ASTNode} node - The current AST node.
+ * @returns {void}
+ */
+function processCodePathToExit(analyzer, node) {
+ const codePath = analyzer.codePath;
+ const state = CodePath.getState(codePath);
+ let dontForward = false;
+
+ switch (node.type) {
+ case "IfStatement":
+ case "ConditionalExpression":
+ case "LogicalExpression":
+ state.popChoiceContext();
+ break;
+
+ case "SwitchStatement":
+ state.popSwitchContext();
+ break;
+
+ case "SwitchCase":
+
+ /*
+ * This is the same as the process at the 1st `consequent` node in
+ * `preprocess` function.
+ * Must do if this `consequent` is empty.
+ */
+ if (node.consequent.length === 0) {
+ state.makeSwitchCaseBody(true, !node.test);
+ }
+ if (state.forkContext.reachable) {
+ dontForward = true;
+ }
+ break;
+
+ case "TryStatement":
+ state.popTryContext();
+ break;
+
+ case "BreakStatement":
+ forwardCurrentToHead(analyzer, node);
+ state.makeBreak(node.label && node.label.name);
+ dontForward = true;
+ break;
+
+ case "ContinueStatement":
+ forwardCurrentToHead(analyzer, node);
+ state.makeContinue(node.label && node.label.name);
+ dontForward = true;
+ break;
+
+ case "ReturnStatement":
+ forwardCurrentToHead(analyzer, node);
+ state.makeReturn();
+ dontForward = true;
+ break;
+
+ case "ThrowStatement":
+ forwardCurrentToHead(analyzer, node);
+ state.makeThrow();
+ dontForward = true;
+ break;
+
+ case "Identifier":
+ if (isIdentifierReference(node)) {
+ state.makeFirstThrowablePathInTryBlock();
+ dontForward = true;
+ }
+ break;
+
+ case "CallExpression":
+ case "MemberExpression":
+ case "NewExpression":
+ state.makeFirstThrowablePathInTryBlock();
+ break;
+
+ case "WhileStatement":
+ case "DoWhileStatement":
+ case "ForStatement":
+ case "ForInStatement":
+ case "ForOfStatement":
+ state.popLoopContext();
+ break;
+
+ case "AssignmentPattern":
+ state.popForkContext();
+ break;
+
+ case "LabeledStatement":
+ if (!astUtils.isBreakableStatement(node.body)) {
+ state.popBreakContext();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Emits onCodePathSegmentStart events if updated.
+ if (!dontForward) {
+ forwardCurrentToHead(analyzer, node);
+ }
+ debug.dumpState(node, state, true);
+}
+
+/**
+ * Updates the code path to finalize the current code path.
+ *
+ * @param {CodePathAnalyzer} analyzer - The instance.
+ * @param {ASTNode} node - The current AST node.
+ * @returns {void}
+ */
+function postprocess(analyzer, node) {
+ switch (node.type) {
+ case "Program":
+ case "FunctionDeclaration":
+ case "FunctionExpression":
+ case "ArrowFunctionExpression": {
+ let codePath = analyzer.codePath;
+
+ // Mark the current path as the final node.
+ CodePath.getState(codePath).makeFinal();
+
+ // Emits onCodePathSegmentEnd event of the current segments.
+ leaveFromCurrentSegment(analyzer, node);
+
+ // Emits onCodePathEnd event of this code path.
+ debug.dump(`onCodePathEnd ${codePath.id}`);
+ analyzer.emitter.emit("onCodePathEnd", codePath, node);
+ debug.dumpDot(codePath);
+
+ codePath = analyzer.codePath = analyzer.codePath.upper;
+ if (codePath) {
+ debug.dumpState(node, CodePath.getState(codePath), true);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * The class to analyze code paths.
+ * This class implements the EventGenerator interface.
+ */
+class CodePathAnalyzer {
+
+ /**
+ * @param {EventGenerator} eventGenerator - An event generator to wrap.
+ */
+ constructor(eventGenerator) {
+ this.original = eventGenerator;
+ this.emitter = eventGenerator.emitter;
+ this.codePath = null;
+ this.idGenerator = new IdGenerator("s");
+ this.currentNode = null;
+ this.onLooped = this.onLooped.bind(this);
+ }
+
+ /**
+ * Does the process to enter a given AST node.
+ * This updates state of analysis and calls `enterNode` of the wrapped.
+ *
+ * @param {ASTNode} node - A node which is entering.
+ * @returns {void}
+ */
+ enterNode(node) {
+ this.currentNode = node;
+
+ // Updates the code path due to node's position in its parent node.
+ if (node.parent) {
+ preprocess(this, node);
+ }
+
+ /*
+ * Updates the code path.
+ * And emits onCodePathStart/onCodePathSegmentStart events.
+ */
+ processCodePathToEnter(this, node);
+
+ // Emits node events.
+ this.original.enterNode(node);
+
+ this.currentNode = null;
+ }
+
+ /**
+ * Does the process to leave a given AST node.
+ * This updates state of analysis and calls `leaveNode` of the wrapped.
+ *
+ * @param {ASTNode} node - A node which is leaving.
+ * @returns {void}
+ */
+ leaveNode(node) {
+ this.currentNode = node;
+
+ /*
+ * Updates the code path.
+ * And emits onCodePathStart/onCodePathSegmentStart events.
+ */
+ processCodePathToExit(this, node);
+
+ // Emits node events.
+ this.original.leaveNode(node);
+
+ // Emits the last onCodePathStart/onCodePathSegmentStart events.
+ postprocess(this, node);
+
+ this.currentNode = null;
+ }
+
+ /**
+ * This is called on a code path looped.
+ * Then this raises a looped event.
+ *
+ * @param {CodePathSegment} fromSegment - A segment of prev.
+ * @param {CodePathSegment} toSegment - A segment of next.
+ * @returns {void}
+ */
+ onLooped(fromSegment, toSegment) {
+ if (fromSegment.reachable && toSegment.reachable) {
+ debug.dump(`onCodePathSegmentLoop ${fromSegment.id} -> ${toSegment.id}`);
+ this.emitter.emit(
+ "onCodePathSegmentLoop",
+ fromSegment,
+ toSegment,
+ this.currentNode
+ );
+ }
+ }
+}
+
+module.exports = CodePathAnalyzer;
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/code-path-segment.js b/tools/node_modules/eslint/lib/code-path-analysis/code-path-segment.js
new file mode 100644
index 0000000000..8145f92801
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/code-path-segment.js
@@ -0,0 +1,245 @@
+/**
+ * @fileoverview A class of the code path segment.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const debug = require("./debug-helpers");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given segment is reachable.
+ *
+ * @param {CodePathSegment} segment - A segment to check.
+ * @returns {boolean} `true` if the segment is reachable.
+ */
+function isReachable(segment) {
+ return segment.reachable;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A code path segment.
+ */
+class CodePathSegment {
+
+ /**
+ * @param {string} id - An identifier.
+ * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
+ * This array includes unreachable segments.
+ * @param {boolean} reachable - A flag which shows this is reachable.
+ */
+ constructor(id, allPrevSegments, reachable) {
+
+ /**
+ * The identifier of this code path.
+ * Rules use it to store additional information of each rule.
+ * @type {string}
+ */
+ this.id = id;
+
+ /**
+ * An array of the next segments.
+ * @type {CodePathSegment[]}
+ */
+ this.nextSegments = [];
+
+ /**
+ * An array of the previous segments.
+ * @type {CodePathSegment[]}
+ */
+ this.prevSegments = allPrevSegments.filter(isReachable);
+
+ /**
+ * An array of the next segments.
+ * This array includes unreachable segments.
+ * @type {CodePathSegment[]}
+ */
+ this.allNextSegments = [];
+
+ /**
+ * An array of the previous segments.
+ * This array includes unreachable segments.
+ * @type {CodePathSegment[]}
+ */
+ this.allPrevSegments = allPrevSegments;
+
+ /**
+ * A flag which shows this is reachable.
+ * @type {boolean}
+ */
+ this.reachable = reachable;
+
+ // Internal data.
+ Object.defineProperty(this, "internal", {
+ value: {
+ used: false,
+ loopedPrevSegments: []
+ }
+ });
+
+ /* istanbul ignore if */
+ if (debug.enabled) {
+ this.internal.nodes = [];
+ this.internal.exitNodes = [];
+ }
+ }
+
+ /**
+ * Checks a given previous segment is coming from the end of a loop.
+ *
+ * @param {CodePathSegment} segment - A previous segment to check.
+ * @returns {boolean} `true` if the segment is coming from the end of a loop.
+ */
+ isLoopedPrevSegment(segment) {
+ return this.internal.loopedPrevSegments.indexOf(segment) !== -1;
+ }
+
+ /**
+ * Creates the root segment.
+ *
+ * @param {string} id - An identifier.
+ * @returns {CodePathSegment} The created segment.
+ */
+ static newRoot(id) {
+ return new CodePathSegment(id, [], true);
+ }
+
+ /**
+ * Creates a segment that follows given segments.
+ *
+ * @param {string} id - An identifier.
+ * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
+ * @returns {CodePathSegment} The created segment.
+ */
+ static newNext(id, allPrevSegments) {
+ return new CodePathSegment(
+ id,
+ CodePathSegment.flattenUnusedSegments(allPrevSegments),
+ allPrevSegments.some(isReachable)
+ );
+ }
+
+ /**
+ * Creates an unreachable segment that follows given segments.
+ *
+ * @param {string} id - An identifier.
+ * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
+ * @returns {CodePathSegment} The created segment.
+ */
+ static newUnreachable(id, allPrevSegments) {
+ const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
+
+ /*
+ * In `if (a) return a; foo();` case, the unreachable segment preceded by
+ * the return statement is not used but must not be remove.
+ */
+ CodePathSegment.markUsed(segment);
+
+ return segment;
+ }
+
+ /**
+ * Creates a segment that follows given segments.
+ * This factory method does not connect with `allPrevSegments`.
+ * But this inherits `reachable` flag.
+ *
+ * @param {string} id - An identifier.
+ * @param {CodePathSegment[]} allPrevSegments - An array of the previous segments.
+ * @returns {CodePathSegment} The created segment.
+ */
+ static newDisconnected(id, allPrevSegments) {
+ return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
+ }
+
+ /**
+ * Makes a given segment being used.
+ *
+ * And this function registers the segment into the previous segments as a next.
+ *
+ * @param {CodePathSegment} segment - A segment to mark.
+ * @returns {void}
+ */
+ static markUsed(segment) {
+ if (segment.internal.used) {
+ return;
+ }
+ segment.internal.used = true;
+
+ let i;
+
+ if (segment.reachable) {
+ for (i = 0; i < segment.allPrevSegments.length; ++i) {
+ const prevSegment = segment.allPrevSegments[i];
+
+ prevSegment.allNextSegments.push(segment);
+ prevSegment.nextSegments.push(segment);
+ }
+ } else {
+ for (i = 0; i < segment.allPrevSegments.length; ++i) {
+ segment.allPrevSegments[i].allNextSegments.push(segment);
+ }
+ }
+ }
+
+ /**
+ * Marks a previous segment as looped.
+ *
+ * @param {CodePathSegment} segment - A segment.
+ * @param {CodePathSegment} prevSegment - A previous segment to mark.
+ * @returns {void}
+ */
+ static markPrevSegmentAsLooped(segment, prevSegment) {
+ segment.internal.loopedPrevSegments.push(prevSegment);
+ }
+
+ /**
+ * Replaces unused segments with the previous segments of each unused segment.
+ *
+ * @param {CodePathSegment[]} segments - An array of segments to replace.
+ * @returns {CodePathSegment[]} The replaced array.
+ */
+ static flattenUnusedSegments(segments) {
+ const done = Object.create(null);
+ const retv = [];
+
+ for (let i = 0; i < segments.length; ++i) {
+ const segment = segments[i];
+
+ // Ignores duplicated.
+ if (done[segment.id]) {
+ continue;
+ }
+
+ // Use previous segments if unused.
+ if (!segment.internal.used) {
+ for (let j = 0; j < segment.allPrevSegments.length; ++j) {
+ const prevSegment = segment.allPrevSegments[j];
+
+ if (!done[prevSegment.id]) {
+ done[prevSegment.id] = true;
+ retv.push(prevSegment);
+ }
+ }
+ } else {
+ done[segment.id] = true;
+ retv.push(segment);
+ }
+ }
+
+ return retv;
+ }
+}
+
+module.exports = CodePathSegment;
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/code-path-state.js b/tools/node_modules/eslint/lib/code-path-analysis/code-path-state.js
new file mode 100644
index 0000000000..0c31e2072b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/code-path-state.js
@@ -0,0 +1,1440 @@
+/**
+ * @fileoverview A class to manage state of generating a code path.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const CodePathSegment = require("./code-path-segment"),
+ ForkContext = require("./fork-context");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Adds given segments into the `dest` array.
+ * If the `others` array does not includes the given segments, adds to the `all`
+ * array as well.
+ *
+ * This adds only reachable and used segments.
+ *
+ * @param {CodePathSegment[]} dest - A destination array (`returnedSegments` or `thrownSegments`).
+ * @param {CodePathSegment[]} others - Another destination array (`returnedSegments` or `thrownSegments`).
+ * @param {CodePathSegment[]} all - The unified destination array (`finalSegments`).
+ * @param {CodePathSegment[]} segments - Segments to add.
+ * @returns {void}
+ */
+function addToReturnedOrThrown(dest, others, all, segments) {
+ for (let i = 0; i < segments.length; ++i) {
+ const segment = segments[i];
+
+ dest.push(segment);
+ if (others.indexOf(segment) === -1) {
+ all.push(segment);
+ }
+ }
+}
+
+/**
+ * Gets a loop-context for a `continue` statement.
+ *
+ * @param {CodePathState} state - A state to get.
+ * @param {string} label - The label of a `continue` statement.
+ * @returns {LoopContext} A loop-context for a `continue` statement.
+ */
+function getContinueContext(state, label) {
+ if (!label) {
+ return state.loopContext;
+ }
+
+ let context = state.loopContext;
+
+ while (context) {
+ if (context.label === label) {
+ return context;
+ }
+ context = context.upper;
+ }
+
+ /* istanbul ignore next: foolproof (syntax error) */
+ return null;
+}
+
+/**
+ * Gets a context for a `break` statement.
+ *
+ * @param {CodePathState} state - A state to get.
+ * @param {string} label - The label of a `break` statement.
+ * @returns {LoopContext|SwitchContext} A context for a `break` statement.
+ */
+function getBreakContext(state, label) {
+ let context = state.breakContext;
+
+ while (context) {
+ if (label ? context.label === label : context.breakable) {
+ return context;
+ }
+ context = context.upper;
+ }
+
+ /* istanbul ignore next: foolproof (syntax error) */
+ return null;
+}
+
+/**
+ * Gets a context for a `return` statement.
+ *
+ * @param {CodePathState} state - A state to get.
+ * @returns {TryContext|CodePathState} A context for a `return` statement.
+ */
+function getReturnContext(state) {
+ let context = state.tryContext;
+
+ while (context) {
+ if (context.hasFinalizer && context.position !== "finally") {
+ return context;
+ }
+ context = context.upper;
+ }
+
+ return state;
+}
+
+/**
+ * Gets a context for a `throw` statement.
+ *
+ * @param {CodePathState} state - A state to get.
+ * @returns {TryContext|CodePathState} A context for a `throw` statement.
+ */
+function getThrowContext(state) {
+ let context = state.tryContext;
+
+ while (context) {
+ if (context.position === "try" ||
+ (context.hasFinalizer && context.position === "catch")
+ ) {
+ return context;
+ }
+ context = context.upper;
+ }
+
+ return state;
+}
+
+/**
+ * Removes a given element from a given array.
+ *
+ * @param {any[]} xs - An array to remove the specific element.
+ * @param {any} x - An element to be removed.
+ * @returns {void}
+ */
+function remove(xs, x) {
+ xs.splice(xs.indexOf(x), 1);
+}
+
+/**
+ * Disconnect given segments.
+ *
+ * This is used in a process for switch statements.
+ * If there is the "default" chunk before other cases, the order is different
+ * between node's and running's.
+ *
+ * @param {CodePathSegment[]} prevSegments - Forward segments to disconnect.
+ * @param {CodePathSegment[]} nextSegments - Backward segments to disconnect.
+ * @returns {void}
+ */
+function removeConnection(prevSegments, nextSegments) {
+ for (let i = 0; i < prevSegments.length; ++i) {
+ const prevSegment = prevSegments[i];
+ const nextSegment = nextSegments[i];
+
+ remove(prevSegment.nextSegments, nextSegment);
+ remove(prevSegment.allNextSegments, nextSegment);
+ remove(nextSegment.prevSegments, prevSegment);
+ remove(nextSegment.allPrevSegments, prevSegment);
+ }
+}
+
+/**
+ * Creates looping path.
+ *
+ * @param {CodePathState} state - The instance.
+ * @param {CodePathSegment[]} fromSegments - Segments which are source.
+ * @param {CodePathSegment[]} toSegments - Segments which are destination.
+ * @returns {void}
+ */
+function makeLooped(state, fromSegments, toSegments) {
+ fromSegments = CodePathSegment.flattenUnusedSegments(fromSegments);
+ toSegments = CodePathSegment.flattenUnusedSegments(toSegments);
+
+ const end = Math.min(fromSegments.length, toSegments.length);
+
+ for (let i = 0; i < end; ++i) {
+ const fromSegment = fromSegments[i];
+ const toSegment = toSegments[i];
+
+ if (toSegment.reachable) {
+ fromSegment.nextSegments.push(toSegment);
+ }
+ if (fromSegment.reachable) {
+ toSegment.prevSegments.push(fromSegment);
+ }
+ fromSegment.allNextSegments.push(toSegment);
+ toSegment.allPrevSegments.push(fromSegment);
+
+ if (toSegment.allPrevSegments.length >= 2) {
+ CodePathSegment.markPrevSegmentAsLooped(toSegment, fromSegment);
+ }
+
+ state.notifyLooped(fromSegment, toSegment);
+ }
+}
+
+/**
+ * Finalizes segments of `test` chunk of a ForStatement.
+ *
+ * - Adds `false` paths to paths which are leaving from the loop.
+ * - Sets `true` paths to paths which go to the body.
+ *
+ * @param {LoopContext} context - A loop context to modify.
+ * @param {ChoiceContext} choiceContext - A choice context of this loop.
+ * @param {CodePathSegment[]} head - The current head paths.
+ * @returns {void}
+ */
+function finalizeTestSegmentsOfFor(context, choiceContext, head) {
+ if (!choiceContext.processed) {
+ choiceContext.trueForkContext.add(head);
+ choiceContext.falseForkContext.add(head);
+ }
+
+ if (context.test !== true) {
+ context.brokenForkContext.addAll(choiceContext.falseForkContext);
+ }
+ context.endOfTestSegments = choiceContext.trueForkContext.makeNext(0, -1);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A class which manages state to analyze code paths.
+ */
+class CodePathState {
+
+ /**
+ * @param {IdGenerator} idGenerator - An id generator to generate id for code
+ * path segments.
+ * @param {Function} onLooped - A callback function to notify looping.
+ */
+ constructor(idGenerator, onLooped) {
+ this.idGenerator = idGenerator;
+ this.notifyLooped = onLooped;
+ this.forkContext = ForkContext.newRoot(idGenerator);
+ this.choiceContext = null;
+ this.switchContext = null;
+ this.tryContext = null;
+ this.loopContext = null;
+ this.breakContext = null;
+
+ this.currentSegments = [];
+ this.initialSegment = this.forkContext.head[0];
+
+ // returnedSegments and thrownSegments push elements into finalSegments also.
+ const final = this.finalSegments = [];
+ const returned = this.returnedForkContext = [];
+ const thrown = this.thrownForkContext = [];
+
+ returned.add = addToReturnedOrThrown.bind(null, returned, thrown, final);
+ thrown.add = addToReturnedOrThrown.bind(null, thrown, returned, final);
+ }
+
+ /**
+ * The head segments.
+ * @type {CodePathSegment[]}
+ */
+ get headSegments() {
+ return this.forkContext.head;
+ }
+
+ /**
+ * The parent forking context.
+ * This is used for the root of new forks.
+ * @type {ForkContext}
+ */
+ get parentForkContext() {
+ const current = this.forkContext;
+
+ return current && current.upper;
+ }
+
+ /**
+ * Creates and stacks new forking context.
+ *
+ * @param {boolean} forkLeavingPath - A flag which shows being in a
+ * "finally" block.
+ * @returns {ForkContext} The created context.
+ */
+ pushForkContext(forkLeavingPath) {
+ this.forkContext = ForkContext.newEmpty(
+ this.forkContext,
+ forkLeavingPath
+ );
+
+ return this.forkContext;
+ }
+
+ /**
+ * Pops and merges the last forking context.
+ * @returns {ForkContext} The last context.
+ */
+ popForkContext() {
+ const lastContext = this.forkContext;
+
+ this.forkContext = lastContext.upper;
+ this.forkContext.replaceHead(lastContext.makeNext(0, -1));
+
+ return lastContext;
+ }
+
+ /**
+ * Creates a new path.
+ * @returns {void}
+ */
+ forkPath() {
+ this.forkContext.add(this.parentForkContext.makeNext(-1, -1));
+ }
+
+ /**
+ * Creates a bypass path.
+ * This is used for such as IfStatement which does not have "else" chunk.
+ *
+ * @returns {void}
+ */
+ forkBypassPath() {
+ this.forkContext.add(this.parentForkContext.head);
+ }
+
+ //--------------------------------------------------------------------------
+ // ConditionalExpression, LogicalExpression, IfStatement
+ //--------------------------------------------------------------------------
+
+ /**
+ * Creates a context for ConditionalExpression, LogicalExpression,
+ * IfStatement, WhileStatement, DoWhileStatement, or ForStatement.
+ *
+ * LogicalExpressions have cases that it goes different paths between the
+ * `true` case and the `false` case.
+ *
+ * For Example:
+ *
+ * if (a || b) {
+ * foo();
+ * } else {
+ * bar();
+ * }
+ *
+ * In this case, `b` is evaluated always in the code path of the `else`
+ * block, but it's not so in the code path of the `if` block.
+ * So there are 3 paths.
+ *
+ * a -> foo();
+ * a -> b -> foo();
+ * a -> b -> bar();
+ *
+ * @param {string} kind - A kind string.
+ * If the new context is LogicalExpression's, this is `"&&"` or `"||"`.
+ * If it's IfStatement's or ConditionalExpression's, this is `"test"`.
+ * Otherwise, this is `"loop"`.
+ * @param {boolean} isForkingAsResult - A flag that shows that goes different
+ * paths between `true` and `false`.
+ * @returns {void}
+ */
+ pushChoiceContext(kind, isForkingAsResult) {
+ this.choiceContext = {
+ upper: this.choiceContext,
+ kind,
+ isForkingAsResult,
+ trueForkContext: ForkContext.newEmpty(this.forkContext),
+ falseForkContext: ForkContext.newEmpty(this.forkContext),
+ processed: false
+ };
+ }
+
+ /**
+ * Pops the last choice context and finalizes it.
+ *
+ * @returns {ChoiceContext} The popped context.
+ */
+ popChoiceContext() {
+ const context = this.choiceContext;
+
+ this.choiceContext = context.upper;
+
+ const forkContext = this.forkContext;
+ const headSegments = forkContext.head;
+
+ switch (context.kind) {
+ case "&&":
+ case "||":
+
+ /*
+ * If any result were not transferred from child contexts,
+ * this sets the head segments to both cases.
+ * The head segments are the path of the right-hand operand.
+ */
+ if (!context.processed) {
+ context.trueForkContext.add(headSegments);
+ context.falseForkContext.add(headSegments);
+ }
+
+ /*
+ * Transfers results to upper context if this context is in
+ * test chunk.
+ */
+ if (context.isForkingAsResult) {
+ const parentContext = this.choiceContext;
+
+ parentContext.trueForkContext.addAll(context.trueForkContext);
+ parentContext.falseForkContext.addAll(context.falseForkContext);
+ parentContext.processed = true;
+
+ return context;
+ }
+
+ break;
+
+ case "test":
+ if (!context.processed) {
+
+ /*
+ * The head segments are the path of the `if` block here.
+ * Updates the `true` path with the end of the `if` block.
+ */
+ context.trueForkContext.clear();
+ context.trueForkContext.add(headSegments);
+ } else {
+
+ /*
+ * The head segments are the path of the `else` block here.
+ * Updates the `false` path with the end of the `else`
+ * block.
+ */
+ context.falseForkContext.clear();
+ context.falseForkContext.add(headSegments);
+ }
+
+ break;
+
+ case "loop":
+
+ /*
+ * Loops are addressed in popLoopContext().
+ * This is called from popLoopContext().
+ */
+ return context;
+
+ /* istanbul ignore next */
+ default:
+ throw new Error("unreachable");
+ }
+
+ // Merges all paths.
+ const prevForkContext = context.trueForkContext;
+
+ prevForkContext.addAll(context.falseForkContext);
+ forkContext.replaceHead(prevForkContext.makeNext(0, -1));
+
+ return context;
+ }
+
+ /**
+ * Makes a code path segment of the right-hand operand of a logical
+ * expression.
+ *
+ * @returns {void}
+ */
+ makeLogicalRight() {
+ const context = this.choiceContext;
+ const forkContext = this.forkContext;
+
+ if (context.processed) {
+
+ /*
+ * This got segments already from the child choice context.
+ * Creates the next path from own true/false fork context.
+ */
+ const prevForkContext =
+ context.kind === "&&" ? context.trueForkContext
+ /* kind === "||" */ : context.falseForkContext;
+
+ forkContext.replaceHead(prevForkContext.makeNext(0, -1));
+ prevForkContext.clear();
+
+ context.processed = false;
+ } else {
+
+ /*
+ * This did not get segments from the child choice context.
+ * So addresses the head segments.
+ * The head segments are the path of the left-hand operand.
+ */
+ if (context.kind === "&&") {
+
+ // The path does short-circuit if false.
+ context.falseForkContext.add(forkContext.head);
+ } else {
+
+ // The path does short-circuit if true.
+ context.trueForkContext.add(forkContext.head);
+ }
+
+ forkContext.replaceHead(forkContext.makeNext(-1, -1));
+ }
+ }
+
+ /**
+ * Makes a code path segment of the `if` block.
+ *
+ * @returns {void}
+ */
+ makeIfConsequent() {
+ const context = this.choiceContext;
+ const forkContext = this.forkContext;
+
+ /*
+ * If any result were not transferred from child contexts,
+ * this sets the head segments to both cases.
+ * The head segments are the path of the test expression.
+ */
+ if (!context.processed) {
+ context.trueForkContext.add(forkContext.head);
+ context.falseForkContext.add(forkContext.head);
+ }
+
+ context.processed = false;
+
+ // Creates new path from the `true` case.
+ forkContext.replaceHead(
+ context.trueForkContext.makeNext(0, -1)
+ );
+ }
+
+ /**
+ * Makes a code path segment of the `else` block.
+ *
+ * @returns {void}
+ */
+ makeIfAlternate() {
+ const context = this.choiceContext;
+ const forkContext = this.forkContext;
+
+ /*
+ * The head segments are the path of the `if` block.
+ * Updates the `true` path with the end of the `if` block.
+ */
+ context.trueForkContext.clear();
+ context.trueForkContext.add(forkContext.head);
+ context.processed = true;
+
+ // Creates new path from the `false` case.
+ forkContext.replaceHead(
+ context.falseForkContext.makeNext(0, -1)
+ );
+ }
+
+ //--------------------------------------------------------------------------
+ // SwitchStatement
+ //--------------------------------------------------------------------------
+
+ /**
+ * Creates a context object of SwitchStatement and stacks it.
+ *
+ * @param {boolean} hasCase - `true` if the switch statement has one or more
+ * case parts.
+ * @param {string|null} label - The label text.
+ * @returns {void}
+ */
+ pushSwitchContext(hasCase, label) {
+ this.switchContext = {
+ upper: this.switchContext,
+ hasCase,
+ defaultSegments: null,
+ defaultBodySegments: null,
+ foundDefault: false,
+ lastIsDefault: false,
+ countForks: 0
+ };
+
+ this.pushBreakContext(true, label);
+ }
+
+ /**
+ * Pops the last context of SwitchStatement and finalizes it.
+ *
+ * - Disposes all forking stack for `case` and `default`.
+ * - Creates the next code path segment from `context.brokenForkContext`.
+ * - If the last `SwitchCase` node is not a `default` part, creates a path
+ * to the `default` body.
+ *
+ * @returns {void}
+ */
+ popSwitchContext() {
+ const context = this.switchContext;
+
+ this.switchContext = context.upper;
+
+ const forkContext = this.forkContext;
+ const brokenForkContext = this.popBreakContext().brokenForkContext;
+
+ if (context.countForks === 0) {
+
+ /*
+ * When there is only one `default` chunk and there is one or more
+ * `break` statements, even if forks are nothing, it needs to merge
+ * those.
+ */
+ if (!brokenForkContext.empty) {
+ brokenForkContext.add(forkContext.makeNext(-1, -1));
+ forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
+ }
+
+ return;
+ }
+
+ const lastSegments = forkContext.head;
+
+ this.forkBypassPath();
+ const lastCaseSegments = forkContext.head;
+
+ /*
+ * `brokenForkContext` is used to make the next segment.
+ * It must add the last segment into `brokenForkContext`.
+ */
+ brokenForkContext.add(lastSegments);
+
+ /*
+ * A path which is failed in all case test should be connected to path
+ * of `default` chunk.
+ */
+ if (!context.lastIsDefault) {
+ if (context.defaultBodySegments) {
+
+ /*
+ * Remove a link from `default` label to its chunk.
+ * It's false route.
+ */
+ removeConnection(context.defaultSegments, context.defaultBodySegments);
+ makeLooped(this, lastCaseSegments, context.defaultBodySegments);
+ } else {
+
+ /*
+ * It handles the last case body as broken if `default` chunk
+ * does not exist.
+ */
+ brokenForkContext.add(lastCaseSegments);
+ }
+ }
+
+ // Pops the segment context stack until the entry segment.
+ for (let i = 0; i < context.countForks; ++i) {
+ this.forkContext = this.forkContext.upper;
+ }
+
+ /*
+ * Creates a path from all brokenForkContext paths.
+ * This is a path after switch statement.
+ */
+ this.forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
+ }
+
+ /**
+ * Makes a code path segment for a `SwitchCase` node.
+ *
+ * @param {boolean} isEmpty - `true` if the body is empty.
+ * @param {boolean} isDefault - `true` if the body is the default case.
+ * @returns {void}
+ */
+ makeSwitchCaseBody(isEmpty, isDefault) {
+ const context = this.switchContext;
+
+ if (!context.hasCase) {
+ return;
+ }
+
+ /*
+ * Merge forks.
+ * The parent fork context has two segments.
+ * Those are from the current case and the body of the previous case.
+ */
+ const parentForkContext = this.forkContext;
+ const forkContext = this.pushForkContext();
+
+ forkContext.add(parentForkContext.makeNext(0, -1));
+
+ /*
+ * Save `default` chunk info.
+ * If the `default` label is not at the last, we must make a path from
+ * the last `case` to the `default` chunk.
+ */
+ if (isDefault) {
+ context.defaultSegments = parentForkContext.head;
+ if (isEmpty) {
+ context.foundDefault = true;
+ } else {
+ context.defaultBodySegments = forkContext.head;
+ }
+ } else {
+ if (!isEmpty && context.foundDefault) {
+ context.foundDefault = false;
+ context.defaultBodySegments = forkContext.head;
+ }
+ }
+
+ context.lastIsDefault = isDefault;
+ context.countForks += 1;
+ }
+
+ //--------------------------------------------------------------------------
+ // TryStatement
+ //--------------------------------------------------------------------------
+
+ /**
+ * Creates a context object of TryStatement and stacks it.
+ *
+ * @param {boolean} hasFinalizer - `true` if the try statement has a
+ * `finally` block.
+ * @returns {void}
+ */
+ pushTryContext(hasFinalizer) {
+ this.tryContext = {
+ upper: this.tryContext,
+ position: "try",
+ hasFinalizer,
+
+ returnedForkContext: hasFinalizer
+ ? ForkContext.newEmpty(this.forkContext)
+ : null,
+
+ thrownForkContext: ForkContext.newEmpty(this.forkContext),
+ lastOfTryIsReachable: false,
+ lastOfCatchIsReachable: false
+ };
+ }
+
+ /**
+ * Pops the last context of TryStatement and finalizes it.
+ *
+ * @returns {void}
+ */
+ popTryContext() {
+ const context = this.tryContext;
+
+ this.tryContext = context.upper;
+
+ if (context.position === "catch") {
+
+ // Merges two paths from the `try` block and `catch` block merely.
+ this.popForkContext();
+ return;
+ }
+
+ /*
+ * The following process is executed only when there is the `finally`
+ * block.
+ */
+
+ const returned = context.returnedForkContext;
+ const thrown = context.thrownForkContext;
+
+ if (returned.empty && thrown.empty) {
+ return;
+ }
+
+ // Separate head to normal paths and leaving paths.
+ const headSegments = this.forkContext.head;
+
+ this.forkContext = this.forkContext.upper;
+ const normalSegments = headSegments.slice(0, headSegments.length / 2 | 0);
+ const leavingSegments = headSegments.slice(headSegments.length / 2 | 0);
+
+ // Forwards the leaving path to upper contexts.
+ if (!returned.empty) {
+ getReturnContext(this).returnedForkContext.add(leavingSegments);
+ }
+ if (!thrown.empty) {
+ getThrowContext(this).thrownForkContext.add(leavingSegments);
+ }
+
+ // Sets the normal path as the next.
+ this.forkContext.replaceHead(normalSegments);
+
+ /*
+ * If both paths of the `try` block and the `catch` block are
+ * unreachable, the next path becomes unreachable as well.
+ */
+ if (!context.lastOfTryIsReachable && !context.lastOfCatchIsReachable) {
+ this.forkContext.makeUnreachable();
+ }
+ }
+
+ /**
+ * Makes a code path segment for a `catch` block.
+ *
+ * @returns {void}
+ */
+ makeCatchBlock() {
+ const context = this.tryContext;
+ const forkContext = this.forkContext;
+ const thrown = context.thrownForkContext;
+
+ // Update state.
+ context.position = "catch";
+ context.thrownForkContext = ForkContext.newEmpty(forkContext);
+ context.lastOfTryIsReachable = forkContext.reachable;
+
+ // Merge thrown paths.
+ thrown.add(forkContext.head);
+ const thrownSegments = thrown.makeNext(0, -1);
+
+ // Fork to a bypass and the merged thrown path.
+ this.pushForkContext();
+ this.forkBypassPath();
+ this.forkContext.add(thrownSegments);
+ }
+
+ /**
+ * Makes a code path segment for a `finally` block.
+ *
+ * In the `finally` block, parallel paths are created. The parallel paths
+ * are used as leaving-paths. The leaving-paths are paths from `return`
+ * statements and `throw` statements in a `try` block or a `catch` block.
+ *
+ * @returns {void}
+ */
+ makeFinallyBlock() {
+ const context = this.tryContext;
+ let forkContext = this.forkContext;
+ const returned = context.returnedForkContext;
+ const thrown = context.thrownForkContext;
+ const headOfLeavingSegments = forkContext.head;
+
+ // Update state.
+ if (context.position === "catch") {
+
+ // Merges two paths from the `try` block and `catch` block.
+ this.popForkContext();
+ forkContext = this.forkContext;
+
+ context.lastOfCatchIsReachable = forkContext.reachable;
+ } else {
+ context.lastOfTryIsReachable = forkContext.reachable;
+ }
+ context.position = "finally";
+
+ if (returned.empty && thrown.empty) {
+
+ // This path does not leave.
+ return;
+ }
+
+ /*
+ * Create a parallel segment from merging returned and thrown.
+ * This segment will leave at the end of this finally block.
+ */
+ const segments = forkContext.makeNext(-1, -1);
+
+ for (let i = 0; i < forkContext.count; ++i) {
+ const prevSegsOfLeavingSegment = [headOfLeavingSegments[i]];
+
+ for (let j = 0; j < returned.segmentsList.length; ++j) {
+ prevSegsOfLeavingSegment.push(returned.segmentsList[j][i]);
+ }
+ for (let j = 0; j < thrown.segmentsList.length; ++j) {
+ prevSegsOfLeavingSegment.push(thrown.segmentsList[j][i]);
+ }
+
+ segments.push(
+ CodePathSegment.newNext(
+ this.idGenerator.next(),
+ prevSegsOfLeavingSegment
+ )
+ );
+ }
+
+ this.pushForkContext(true);
+ this.forkContext.add(segments);
+ }
+
+ /**
+ * Makes a code path segment from the first throwable node to the `catch`
+ * block or the `finally` block.
+ *
+ * @returns {void}
+ */
+ makeFirstThrowablePathInTryBlock() {
+ const forkContext = this.forkContext;
+
+ if (!forkContext.reachable) {
+ return;
+ }
+
+ const context = getThrowContext(this);
+
+ if (context === this ||
+ context.position !== "try" ||
+ !context.thrownForkContext.empty
+ ) {
+ return;
+ }
+
+ context.thrownForkContext.add(forkContext.head);
+ forkContext.replaceHead(forkContext.makeNext(-1, -1));
+ }
+
+ //--------------------------------------------------------------------------
+ // Loop Statements
+ //--------------------------------------------------------------------------
+
+ /**
+ * Creates a context object of a loop statement and stacks it.
+ *
+ * @param {string} type - The type of the node which was triggered. One of
+ * `WhileStatement`, `DoWhileStatement`, `ForStatement`, `ForInStatement`,
+ * and `ForStatement`.
+ * @param {string|null} label - A label of the node which was triggered.
+ * @returns {void}
+ */
+ pushLoopContext(type, label) {
+ const forkContext = this.forkContext;
+ const breakContext = this.pushBreakContext(true, label);
+
+ switch (type) {
+ case "WhileStatement":
+ this.pushChoiceContext("loop", false);
+ this.loopContext = {
+ upper: this.loopContext,
+ type,
+ label,
+ test: void 0,
+ continueDestSegments: null,
+ brokenForkContext: breakContext.brokenForkContext
+ };
+ break;
+
+ case "DoWhileStatement":
+ this.pushChoiceContext("loop", false);
+ this.loopContext = {
+ upper: this.loopContext,
+ type,
+ label,
+ test: void 0,
+ entrySegments: null,
+ continueForkContext: ForkContext.newEmpty(forkContext),
+ brokenForkContext: breakContext.brokenForkContext
+ };
+ break;
+
+ case "ForStatement":
+ this.pushChoiceContext("loop", false);
+ this.loopContext = {
+ upper: this.loopContext,
+ type,
+ label,
+ test: void 0,
+ endOfInitSegments: null,
+ testSegments: null,
+ endOfTestSegments: null,
+ updateSegments: null,
+ endOfUpdateSegments: null,
+ continueDestSegments: null,
+ brokenForkContext: breakContext.brokenForkContext
+ };
+ break;
+
+ case "ForInStatement":
+ case "ForOfStatement":
+ this.loopContext = {
+ upper: this.loopContext,
+ type,
+ label,
+ prevSegments: null,
+ leftSegments: null,
+ endOfLeftSegments: null,
+ continueDestSegments: null,
+ brokenForkContext: breakContext.brokenForkContext
+ };
+ break;
+
+ /* istanbul ignore next */
+ default:
+ throw new Error(`unknown type: "${type}"`);
+ }
+ }
+
+ /**
+ * Pops the last context of a loop statement and finalizes it.
+ *
+ * @returns {void}
+ */
+ popLoopContext() {
+ const context = this.loopContext;
+
+ this.loopContext = context.upper;
+
+ const forkContext = this.forkContext;
+ const brokenForkContext = this.popBreakContext().brokenForkContext;
+
+ // Creates a looped path.
+ switch (context.type) {
+ case "WhileStatement":
+ case "ForStatement":
+ this.popChoiceContext();
+ makeLooped(
+ this,
+ forkContext.head,
+ context.continueDestSegments
+ );
+ break;
+
+ case "DoWhileStatement": {
+ const choiceContext = this.popChoiceContext();
+
+ if (!choiceContext.processed) {
+ choiceContext.trueForkContext.add(forkContext.head);
+ choiceContext.falseForkContext.add(forkContext.head);
+ }
+ if (context.test !== true) {
+ brokenForkContext.addAll(choiceContext.falseForkContext);
+ }
+
+ // `true` paths go to looping.
+ const segmentsList = choiceContext.trueForkContext.segmentsList;
+
+ for (let i = 0; i < segmentsList.length; ++i) {
+ makeLooped(
+ this,
+ segmentsList[i],
+ context.entrySegments
+ );
+ }
+ break;
+ }
+
+ case "ForInStatement":
+ case "ForOfStatement":
+ brokenForkContext.add(forkContext.head);
+ makeLooped(
+ this,
+ forkContext.head,
+ context.leftSegments
+ );
+ break;
+
+ /* istanbul ignore next */
+ default:
+ throw new Error("unreachable");
+ }
+
+ // Go next.
+ if (brokenForkContext.empty) {
+ forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
+ } else {
+ forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
+ }
+ }
+
+ /**
+ * Makes a code path segment for the test part of a WhileStatement.
+ *
+ * @param {boolean|undefined} test - The test value (only when constant).
+ * @returns {void}
+ */
+ makeWhileTest(test) {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+ const testSegments = forkContext.makeNext(0, -1);
+
+ // Update state.
+ context.test = test;
+ context.continueDestSegments = testSegments;
+ forkContext.replaceHead(testSegments);
+ }
+
+ /**
+ * Makes a code path segment for the body part of a WhileStatement.
+ *
+ * @returns {void}
+ */
+ makeWhileBody() {
+ const context = this.loopContext;
+ const choiceContext = this.choiceContext;
+ const forkContext = this.forkContext;
+
+ if (!choiceContext.processed) {
+ choiceContext.trueForkContext.add(forkContext.head);
+ choiceContext.falseForkContext.add(forkContext.head);
+ }
+
+ // Update state.
+ if (context.test !== true) {
+ context.brokenForkContext.addAll(choiceContext.falseForkContext);
+ }
+ forkContext.replaceHead(choiceContext.trueForkContext.makeNext(0, -1));
+ }
+
+ /**
+ * Makes a code path segment for the body part of a DoWhileStatement.
+ *
+ * @returns {void}
+ */
+ makeDoWhileBody() {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+ const bodySegments = forkContext.makeNext(-1, -1);
+
+ // Update state.
+ context.entrySegments = bodySegments;
+ forkContext.replaceHead(bodySegments);
+ }
+
+ /**
+ * Makes a code path segment for the test part of a DoWhileStatement.
+ *
+ * @param {boolean|undefined} test - The test value (only when constant).
+ * @returns {void}
+ */
+ makeDoWhileTest(test) {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+
+ context.test = test;
+
+ // Creates paths of `continue` statements.
+ if (!context.continueForkContext.empty) {
+ context.continueForkContext.add(forkContext.head);
+ const testSegments = context.continueForkContext.makeNext(0, -1);
+
+ forkContext.replaceHead(testSegments);
+ }
+ }
+
+ /**
+ * Makes a code path segment for the test part of a ForStatement.
+ *
+ * @param {boolean|undefined} test - The test value (only when constant).
+ * @returns {void}
+ */
+ makeForTest(test) {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+ const endOfInitSegments = forkContext.head;
+ const testSegments = forkContext.makeNext(-1, -1);
+
+ // Update state.
+ context.test = test;
+ context.endOfInitSegments = endOfInitSegments;
+ context.continueDestSegments = context.testSegments = testSegments;
+ forkContext.replaceHead(testSegments);
+ }
+
+ /**
+ * Makes a code path segment for the update part of a ForStatement.
+ *
+ * @returns {void}
+ */
+ makeForUpdate() {
+ const context = this.loopContext;
+ const choiceContext = this.choiceContext;
+ const forkContext = this.forkContext;
+
+ // Make the next paths of the test.
+ if (context.testSegments) {
+ finalizeTestSegmentsOfFor(
+ context,
+ choiceContext,
+ forkContext.head
+ );
+ } else {
+ context.endOfInitSegments = forkContext.head;
+ }
+
+ // Update state.
+ const updateSegments = forkContext.makeDisconnected(-1, -1);
+
+ context.continueDestSegments = context.updateSegments = updateSegments;
+ forkContext.replaceHead(updateSegments);
+ }
+
+ /**
+ * Makes a code path segment for the body part of a ForStatement.
+ *
+ * @returns {void}
+ */
+ makeForBody() {
+ const context = this.loopContext;
+ const choiceContext = this.choiceContext;
+ const forkContext = this.forkContext;
+
+ // Update state.
+ if (context.updateSegments) {
+ context.endOfUpdateSegments = forkContext.head;
+
+ // `update` -> `test`
+ if (context.testSegments) {
+ makeLooped(
+ this,
+ context.endOfUpdateSegments,
+ context.testSegments
+ );
+ }
+ } else if (context.testSegments) {
+ finalizeTestSegmentsOfFor(
+ context,
+ choiceContext,
+ forkContext.head
+ );
+ } else {
+ context.endOfInitSegments = forkContext.head;
+ }
+
+ let bodySegments = context.endOfTestSegments;
+
+ if (!bodySegments) {
+
+ /*
+ * If there is not the `test` part, the `body` path comes from the
+ * `init` part and the `update` part.
+ */
+ const prevForkContext = ForkContext.newEmpty(forkContext);
+
+ prevForkContext.add(context.endOfInitSegments);
+ if (context.endOfUpdateSegments) {
+ prevForkContext.add(context.endOfUpdateSegments);
+ }
+
+ bodySegments = prevForkContext.makeNext(0, -1);
+ }
+ context.continueDestSegments = context.continueDestSegments || bodySegments;
+ forkContext.replaceHead(bodySegments);
+ }
+
+ /**
+ * Makes a code path segment for the left part of a ForInStatement and a
+ * ForOfStatement.
+ *
+ * @returns {void}
+ */
+ makeForInOfLeft() {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+ const leftSegments = forkContext.makeDisconnected(-1, -1);
+
+ // Update state.
+ context.prevSegments = forkContext.head;
+ context.leftSegments = context.continueDestSegments = leftSegments;
+ forkContext.replaceHead(leftSegments);
+ }
+
+ /**
+ * Makes a code path segment for the right part of a ForInStatement and a
+ * ForOfStatement.
+ *
+ * @returns {void}
+ */
+ makeForInOfRight() {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+ const temp = ForkContext.newEmpty(forkContext);
+
+ temp.add(context.prevSegments);
+ const rightSegments = temp.makeNext(-1, -1);
+
+ // Update state.
+ context.endOfLeftSegments = forkContext.head;
+ forkContext.replaceHead(rightSegments);
+ }
+
+ /**
+ * Makes a code path segment for the body part of a ForInStatement and a
+ * ForOfStatement.
+ *
+ * @returns {void}
+ */
+ makeForInOfBody() {
+ const context = this.loopContext;
+ const forkContext = this.forkContext;
+ const temp = ForkContext.newEmpty(forkContext);
+
+ temp.add(context.endOfLeftSegments);
+ const bodySegments = temp.makeNext(-1, -1);
+
+ // Make a path: `right` -> `left`.
+ makeLooped(this, forkContext.head, context.leftSegments);
+
+ // Update state.
+ context.brokenForkContext.add(forkContext.head);
+ forkContext.replaceHead(bodySegments);
+ }
+
+ //--------------------------------------------------------------------------
+ // Control Statements
+ //--------------------------------------------------------------------------
+
+ /**
+ * Creates new context for BreakStatement.
+ *
+ * @param {boolean} breakable - The flag to indicate it can break by
+ * an unlabeled BreakStatement.
+ * @param {string|null} label - The label of this context.
+ * @returns {Object} The new context.
+ */
+ pushBreakContext(breakable, label) {
+ this.breakContext = {
+ upper: this.breakContext,
+ breakable,
+ label,
+ brokenForkContext: ForkContext.newEmpty(this.forkContext)
+ };
+ return this.breakContext;
+ }
+
+ /**
+ * Removes the top item of the break context stack.
+ *
+ * @returns {Object} The removed context.
+ */
+ popBreakContext() {
+ const context = this.breakContext;
+ const forkContext = this.forkContext;
+
+ this.breakContext = context.upper;
+
+ // Process this context here for other than switches and loops.
+ if (!context.breakable) {
+ const brokenForkContext = context.brokenForkContext;
+
+ if (!brokenForkContext.empty) {
+ brokenForkContext.add(forkContext.head);
+ forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
+ }
+ }
+
+ return context;
+ }
+
+ /**
+ * Makes a path for a `break` statement.
+ *
+ * It registers the head segment to a context of `break`.
+ * It makes new unreachable segment, then it set the head with the segment.
+ *
+ * @param {string} label - A label of the break statement.
+ * @returns {void}
+ */
+ makeBreak(label) {
+ const forkContext = this.forkContext;
+
+ if (!forkContext.reachable) {
+ return;
+ }
+
+ const context = getBreakContext(this, label);
+
+ /* istanbul ignore else: foolproof (syntax error) */
+ if (context) {
+ context.brokenForkContext.add(forkContext.head);
+ }
+
+ forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
+ }
+
+ /**
+ * Makes a path for a `continue` statement.
+ *
+ * It makes a looping path.
+ * It makes new unreachable segment, then it set the head with the segment.
+ *
+ * @param {string} label - A label of the continue statement.
+ * @returns {void}
+ */
+ makeContinue(label) {
+ const forkContext = this.forkContext;
+
+ if (!forkContext.reachable) {
+ return;
+ }
+
+ const context = getContinueContext(this, label);
+
+ /* istanbul ignore else: foolproof (syntax error) */
+ if (context) {
+ if (context.continueDestSegments) {
+ makeLooped(this, forkContext.head, context.continueDestSegments);
+
+ // If the context is a for-in/of loop, this effects a break also.
+ if (context.type === "ForInStatement" ||
+ context.type === "ForOfStatement"
+ ) {
+ context.brokenForkContext.add(forkContext.head);
+ }
+ } else {
+ context.continueForkContext.add(forkContext.head);
+ }
+ }
+ forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
+ }
+
+ /**
+ * Makes a path for a `return` statement.
+ *
+ * It registers the head segment to a context of `return`.
+ * It makes new unreachable segment, then it set the head with the segment.
+ *
+ * @returns {void}
+ */
+ makeReturn() {
+ const forkContext = this.forkContext;
+
+ if (forkContext.reachable) {
+ getReturnContext(this).returnedForkContext.add(forkContext.head);
+ forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
+ }
+ }
+
+ /**
+ * Makes a path for a `throw` statement.
+ *
+ * It registers the head segment to a context of `throw`.
+ * It makes new unreachable segment, then it set the head with the segment.
+ *
+ * @returns {void}
+ */
+ makeThrow() {
+ const forkContext = this.forkContext;
+
+ if (forkContext.reachable) {
+ getThrowContext(this).thrownForkContext.add(forkContext.head);
+ forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
+ }
+ }
+
+ /**
+ * Makes the final path.
+ * @returns {void}
+ */
+ makeFinal() {
+ const segments = this.currentSegments;
+
+ if (segments.length > 0 && segments[0].reachable) {
+ this.returnedForkContext.add(segments);
+ }
+ }
+}
+
+module.exports = CodePathState;
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/code-path.js b/tools/node_modules/eslint/lib/code-path-analysis/code-path.js
new file mode 100644
index 0000000000..709a111189
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/code-path.js
@@ -0,0 +1,234 @@
+/**
+ * @fileoverview A class of the code path.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const CodePathState = require("./code-path-state");
+const IdGenerator = require("./id-generator");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A code path.
+ */
+class CodePath {
+
+ /**
+ * @param {string} id - An identifier.
+ * @param {CodePath|null} upper - The code path of the upper function scope.
+ * @param {Function} onLooped - A callback function to notify looping.
+ */
+ constructor(id, upper, onLooped) {
+
+ /**
+ * The identifier of this code path.
+ * Rules use it to store additional information of each rule.
+ * @type {string}
+ */
+ this.id = id;
+
+ /**
+ * The code path of the upper function scope.
+ * @type {CodePath|null}
+ */
+ this.upper = upper;
+
+ /**
+ * The code paths of nested function scopes.
+ * @type {CodePath[]}
+ */
+ this.childCodePaths = [];
+
+ // Initializes internal state.
+ Object.defineProperty(
+ this,
+ "internal",
+ { value: new CodePathState(new IdGenerator(`${id}_`), onLooped) }
+ );
+
+ // Adds this into `childCodePaths` of `upper`.
+ if (upper) {
+ upper.childCodePaths.push(this);
+ }
+ }
+
+ /**
+ * Gets the state of a given code path.
+ *
+ * @param {CodePath} codePath - A code path to get.
+ * @returns {CodePathState} The state of the code path.
+ */
+ static getState(codePath) {
+ return codePath.internal;
+ }
+
+ /**
+ * The initial code path segment.
+ * @type {CodePathSegment}
+ */
+ get initialSegment() {
+ return this.internal.initialSegment;
+ }
+
+ /**
+ * Final code path segments.
+ * This array is a mix of `returnedSegments` and `thrownSegments`.
+ * @type {CodePathSegment[]}
+ */
+ get finalSegments() {
+ return this.internal.finalSegments;
+ }
+
+ /**
+ * Final code path segments which is with `return` statements.
+ * This array contains the last path segment if it's reachable.
+ * Since the reachable last path returns `undefined`.
+ * @type {CodePathSegment[]}
+ */
+ get returnedSegments() {
+ return this.internal.returnedForkContext;
+ }
+
+ /**
+ * Final code path segments which is with `throw` statements.
+ * @type {CodePathSegment[]}
+ */
+ get thrownSegments() {
+ return this.internal.thrownForkContext;
+ }
+
+ /**
+ * Current code path segments.
+ * @type {CodePathSegment[]}
+ */
+ get currentSegments() {
+ return this.internal.currentSegments;
+ }
+
+ /**
+ * Traverses all segments in this code path.
+ *
+ * codePath.traverseSegments(function(segment, controller) {
+ * // do something.
+ * });
+ *
+ * This method enumerates segments in order from the head.
+ *
+ * The `controller` object has two methods.
+ *
+ * - `controller.skip()` - Skip the following segments in this branch.
+ * - `controller.break()` - Skip all following segments.
+ *
+ * @param {Object} [options] - Omittable.
+ * @param {CodePathSegment} [options.first] - The first segment to traverse.
+ * @param {CodePathSegment} [options.last] - The last segment to traverse.
+ * @param {Function} callback - A callback function.
+ * @returns {void}
+ */
+ traverseSegments(options, callback) {
+ if (typeof options === "function") {
+ callback = options;
+ options = null;
+ }
+
+ options = options || {};
+ const startSegment = options.first || this.internal.initialSegment;
+ const lastSegment = options.last;
+
+ let item = null;
+ let index = 0;
+ let end = 0;
+ let segment = null;
+ const visited = Object.create(null);
+ const stack = [[startSegment, 0]];
+ let skippedSegment = null;
+ let broken = false;
+ const controller = {
+ skip() {
+ if (stack.length <= 1) {
+ broken = true;
+ } else {
+ skippedSegment = stack[stack.length - 2][0];
+ }
+ },
+ break() {
+ broken = true;
+ }
+ };
+
+ /**
+ * Checks a given previous segment has been visited.
+ * @param {CodePathSegment} prevSegment - A previous segment to check.
+ * @returns {boolean} `true` if the segment has been visited.
+ */
+ function isVisited(prevSegment) {
+ return (
+ visited[prevSegment.id] ||
+ segment.isLoopedPrevSegment(prevSegment)
+ );
+ }
+
+ while (stack.length > 0) {
+ item = stack[stack.length - 1];
+ segment = item[0];
+ index = item[1];
+
+ if (index === 0) {
+
+ // Skip if this segment has been visited already.
+ if (visited[segment.id]) {
+ stack.pop();
+ continue;
+ }
+
+ // Skip if all previous segments have not been visited.
+ if (segment !== startSegment &&
+ segment.prevSegments.length > 0 &&
+ !segment.prevSegments.every(isVisited)
+ ) {
+ stack.pop();
+ continue;
+ }
+
+ // Reset the flag of skipping if all branches have been skipped.
+ if (skippedSegment && segment.prevSegments.indexOf(skippedSegment) !== -1) {
+ skippedSegment = null;
+ }
+ visited[segment.id] = true;
+
+ // Call the callback when the first time.
+ if (!skippedSegment) {
+ callback.call(this, segment, controller);
+ if (segment === lastSegment) {
+ controller.skip();
+ }
+ if (broken) {
+ break;
+ }
+ }
+ }
+
+ // Update the stack.
+ end = segment.nextSegments.length - 1;
+ if (index < end) {
+ item[1] += 1;
+ stack.push([segment.nextSegments[index], 0]);
+ } else if (index === end) {
+ item[0] = segment.nextSegments[index];
+ item[1] = 0;
+ } else {
+ stack.pop();
+ }
+ }
+ }
+}
+
+module.exports = CodePath;
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js b/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js
new file mode 100644
index 0000000000..9af985ce85
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js
@@ -0,0 +1,200 @@
+/**
+ * @fileoverview Helpers to debug for code path analysis.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const debug = require("debug")("eslint:code-path");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets id of a given segment.
+ * @param {CodePathSegment} segment - A segment to get.
+ * @returns {string} Id of the segment.
+ */
+/* istanbul ignore next */
+function getId(segment) { // eslint-disable-line require-jsdoc
+ return segment.id + (segment.reachable ? "" : "!");
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ /**
+ * A flag that debug dumping is enabled or not.
+ * @type {boolean}
+ */
+ enabled: debug.enabled,
+
+ /**
+ * Dumps given objects.
+ *
+ * @param {...any} args - objects to dump.
+ * @returns {void}
+ */
+ dump: debug,
+
+ /**
+ * Dumps the current analyzing state.
+ *
+ * @param {ASTNode} node - A node to dump.
+ * @param {CodePathState} state - A state to dump.
+ * @param {boolean} leaving - A flag whether or not it's leaving
+ * @returns {void}
+ */
+ dumpState: !debug.enabled ? debug : /* istanbul ignore next */ function(node, state, leaving) {
+ for (let i = 0; i < state.currentSegments.length; ++i) {
+ const segInternal = state.currentSegments[i].internal;
+
+ if (leaving) {
+ segInternal.exitNodes.push(node);
+ } else {
+ segInternal.nodes.push(node);
+ }
+ }
+
+ debug([
+ `${state.currentSegments.map(getId).join(",")})`,
+ `${node.type}${leaving ? ":exit" : ""}`
+ ].join(" "));
+ },
+
+ /**
+ * Dumps a DOT code of a given code path.
+ * The DOT code can be visialized with Graphvis.
+ *
+ * @param {CodePath} codePath - A code path to dump.
+ * @returns {void}
+ * @see http://www.graphviz.org
+ * @see http://www.webgraphviz.com
+ */
+ dumpDot: !debug.enabled ? debug : /* istanbul ignore next */ function(codePath) {
+ let text =
+ "\n" +
+ "digraph {\n" +
+ "node[shape=box,style=\"rounded,filled\",fillcolor=white];\n" +
+ "initial[label=\"\",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
+
+ if (codePath.returnedSegments.length > 0) {
+ text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
+ }
+ if (codePath.thrownSegments.length > 0) {
+ text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize];\n";
+ }
+
+ const traceMap = Object.create(null);
+ const arrows = this.makeDotArrows(codePath, traceMap);
+
+ for (const id in traceMap) { // eslint-disable-line guard-for-in
+ const segment = traceMap[id];
+
+ text += `${id}[`;
+
+ if (segment.reachable) {
+ text += "label=\"";
+ } else {
+ text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
+ }
+
+ if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) {
+ text += [].concat(
+ segment.internal.nodes.map(node => {
+ switch (node.type) {
+ case "Identifier": return `${node.type} (${node.name})`;
+ case "Literal": return `${node.type} (${node.value})`;
+ default: return node.type;
+ }
+ }),
+ segment.internal.exitNodes.map(node => {
+ switch (node.type) {
+ case "Identifier": return `${node.type}:exit (${node.name})`;
+ case "Literal": return `${node.type}:exit (${node.value})`;
+ default: return `${node.type}:exit`;
+ }
+ })
+ ).join("\\n");
+ } else {
+ text += "????";
+ }
+
+ text += "\"];\n";
+ }
+
+ text += `${arrows}\n`;
+ text += "}";
+ debug("DOT", text);
+ },
+
+ /**
+ * Makes a DOT code of a given code path.
+ * The DOT code can be visialized with Graphvis.
+ *
+ * @param {CodePath} codePath - A code path to make DOT.
+ * @param {Object} traceMap - Optional. A map to check whether or not segments had been done.
+ * @returns {string} A DOT code of the code path.
+ */
+ makeDotArrows(codePath, traceMap) {
+ const stack = [[codePath.initialSegment, 0]];
+ const done = traceMap || Object.create(null);
+ let lastId = codePath.initialSegment.id;
+ let text = `initial->${codePath.initialSegment.id}`;
+
+ while (stack.length > 0) {
+ const item = stack.pop();
+ const segment = item[0];
+ const index = item[1];
+
+ if (done[segment.id] && index === 0) {
+ continue;
+ }
+ done[segment.id] = segment;
+
+ const nextSegment = segment.allNextSegments[index];
+
+ if (!nextSegment) {
+ continue;
+ }
+
+ if (lastId === segment.id) {
+ text += `->${nextSegment.id}`;
+ } else {
+ text += `;\n${segment.id}->${nextSegment.id}`;
+ }
+ lastId = nextSegment.id;
+
+ stack.unshift([segment, 1 + index]);
+ stack.push([nextSegment, 0]);
+ }
+
+ codePath.returnedSegments.forEach(finalSegment => {
+ if (lastId === finalSegment.id) {
+ text += "->final";
+ } else {
+ text += `;\n${finalSegment.id}->final`;
+ }
+ lastId = null;
+ });
+
+ codePath.thrownSegments.forEach(finalSegment => {
+ if (lastId === finalSegment.id) {
+ text += "->thrown";
+ } else {
+ text += `;\n${finalSegment.id}->thrown`;
+ }
+ lastId = null;
+ });
+
+ return `${text};`;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/fork-context.js b/tools/node_modules/eslint/lib/code-path-analysis/fork-context.js
new file mode 100644
index 0000000000..4fae6bbb1e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/fork-context.js
@@ -0,0 +1,262 @@
+/**
+ * @fileoverview A class to operate forking.
+ *
+ * This is state of forking.
+ * This has a fork list and manages it.
+ *
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("assert"),
+ CodePathSegment = require("./code-path-segment");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets whether or not a given segment is reachable.
+ *
+ * @param {CodePathSegment} segment - A segment to get.
+ * @returns {boolean} `true` if the segment is reachable.
+ */
+function isReachable(segment) {
+ return segment.reachable;
+}
+
+/**
+ * Creates new segments from the specific range of `context.segmentsList`.
+ *
+ * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
+ * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
+ * This `h` is from `b`, `d`, and `f`.
+ *
+ * @param {ForkContext} context - An instance.
+ * @param {number} begin - The first index of the previous segments.
+ * @param {number} end - The last index of the previous segments.
+ * @param {Function} create - A factory function of new segments.
+ * @returns {CodePathSegment[]} New segments.
+ */
+function makeSegments(context, begin, end, create) {
+ const list = context.segmentsList;
+
+ if (begin < 0) {
+ begin = list.length + begin;
+ }
+ if (end < 0) {
+ end = list.length + end;
+ }
+
+ const segments = [];
+
+ for (let i = 0; i < context.count; ++i) {
+ const allPrevSegments = [];
+
+ for (let j = begin; j <= end; ++j) {
+ allPrevSegments.push(list[j][i]);
+ }
+
+ segments.push(create(context.idGenerator.next(), allPrevSegments));
+ }
+
+ return segments;
+}
+
+/**
+ * `segments` becomes doubly in a `finally` block. Then if a code path exits by a
+ * control statement (such as `break`, `continue`) from the `finally` block, the
+ * destination's segments may be half of the source segments. In that case, this
+ * merges segments.
+ *
+ * @param {ForkContext} context - An instance.
+ * @param {CodePathSegment[]} segments - Segments to merge.
+ * @returns {CodePathSegment[]} The merged segments.
+ */
+function mergeExtraSegments(context, segments) {
+ while (segments.length > context.count) {
+ const merged = [];
+
+ for (let i = 0, length = segments.length / 2 | 0; i < length; ++i) {
+ merged.push(CodePathSegment.newNext(
+ context.idGenerator.next(),
+ [segments[i], segments[i + length]]
+ ));
+ }
+ segments = merged;
+ }
+ return segments;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A class to manage forking.
+ */
+class ForkContext {
+
+ /**
+ * @param {IdGenerator} idGenerator - An identifier generator for segments.
+ * @param {ForkContext|null} upper - An upper fork context.
+ * @param {number} count - A number of parallel segments.
+ */
+ constructor(idGenerator, upper, count) {
+ this.idGenerator = idGenerator;
+ this.upper = upper;
+ this.count = count;
+ this.segmentsList = [];
+ }
+
+ /**
+ * The head segments.
+ * @type {CodePathSegment[]}
+ */
+ get head() {
+ const list = this.segmentsList;
+
+ return list.length === 0 ? [] : list[list.length - 1];
+ }
+
+ /**
+ * A flag which shows empty.
+ * @type {boolean}
+ */
+ get empty() {
+ return this.segmentsList.length === 0;
+ }
+
+ /**
+ * A flag which shows reachable.
+ * @type {boolean}
+ */
+ get reachable() {
+ const segments = this.head;
+
+ return segments.length > 0 && segments.some(isReachable);
+ }
+
+ /**
+ * Creates new segments from this context.
+ *
+ * @param {number} begin - The first index of previous segments.
+ * @param {number} end - The last index of previous segments.
+ * @returns {CodePathSegment[]} New segments.
+ */
+ makeNext(begin, end) {
+ return makeSegments(this, begin, end, CodePathSegment.newNext);
+ }
+
+ /**
+ * Creates new segments from this context.
+ * The new segments is always unreachable.
+ *
+ * @param {number} begin - The first index of previous segments.
+ * @param {number} end - The last index of previous segments.
+ * @returns {CodePathSegment[]} New segments.
+ */
+ makeUnreachable(begin, end) {
+ return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
+ }
+
+ /**
+ * Creates new segments from this context.
+ * The new segments don't have connections for previous segments.
+ * But these inherit the reachable flag from this context.
+ *
+ * @param {number} begin - The first index of previous segments.
+ * @param {number} end - The last index of previous segments.
+ * @returns {CodePathSegment[]} New segments.
+ */
+ makeDisconnected(begin, end) {
+ return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
+ }
+
+ /**
+ * Adds segments into this context.
+ * The added segments become the head.
+ *
+ * @param {CodePathSegment[]} segments - Segments to add.
+ * @returns {void}
+ */
+ add(segments) {
+ assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
+
+ this.segmentsList.push(mergeExtraSegments(this, segments));
+ }
+
+ /**
+ * Replaces the head segments with given segments.
+ * The current head segments are removed.
+ *
+ * @param {CodePathSegment[]} segments - Segments to add.
+ * @returns {void}
+ */
+ replaceHead(segments) {
+ assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
+
+ this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
+ }
+
+ /**
+ * Adds all segments of a given fork context into this context.
+ *
+ * @param {ForkContext} context - A fork context to add.
+ * @returns {void}
+ */
+ addAll(context) {
+ assert(context.count === this.count);
+
+ const source = context.segmentsList;
+
+ for (let i = 0; i < source.length; ++i) {
+ this.segmentsList.push(source[i]);
+ }
+ }
+
+ /**
+ * Clears all secments in this context.
+ *
+ * @returns {void}
+ */
+ clear() {
+ this.segmentsList = [];
+ }
+
+ /**
+ * Creates the root fork context.
+ *
+ * @param {IdGenerator} idGenerator - An identifier generator for segments.
+ * @returns {ForkContext} New fork context.
+ */
+ static newRoot(idGenerator) {
+ const context = new ForkContext(idGenerator, null, 1);
+
+ context.add([CodePathSegment.newRoot(idGenerator.next())]);
+
+ return context;
+ }
+
+ /**
+ * Creates an empty fork context preceded by a given context.
+ *
+ * @param {ForkContext} parentContext - The parent fork context.
+ * @param {boolean} forkLeavingPath - A flag which shows inside of `finally` block.
+ * @returns {ForkContext} New fork context.
+ */
+ static newEmpty(parentContext, forkLeavingPath) {
+ return new ForkContext(
+ parentContext.idGenerator,
+ parentContext,
+ (forkLeavingPath ? 2 : 1) * parentContext.count
+ );
+ }
+}
+
+module.exports = ForkContext;
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/id-generator.js b/tools/node_modules/eslint/lib/code-path-analysis/id-generator.js
new file mode 100644
index 0000000000..062058ddc1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/id-generator.js
@@ -0,0 +1,46 @@
+/**
+ * @fileoverview A class of identifiers generator for code path segments.
+ *
+ * Each rule uses the identifier of code path segments to store additional
+ * information of the code path.
+ *
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A generator for unique ids.
+ */
+class IdGenerator {
+
+ /**
+ * @param {string} prefix - Optional. A prefix of generated ids.
+ */
+ constructor(prefix) {
+ this.prefix = String(prefix);
+ this.n = 0;
+ }
+
+ /**
+ * Generates id.
+ *
+ * @returns {string} A generated id.
+ */
+ next() {
+ this.n = 1 + this.n | 0;
+
+ /* istanbul ignore if */
+ if (this.n < 0) {
+ this.n = 1;
+ }
+
+ return this.prefix + this.n;
+ }
+}
+
+module.exports = IdGenerator;
diff --git a/tools/node_modules/eslint/lib/config.js b/tools/node_modules/eslint/lib/config.js
new file mode 100644
index 0000000000..b66b9f41e0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config.js
@@ -0,0 +1,365 @@
+/**
+ * @fileoverview Responsible for loading config files
+ * @author Seth McLaughlin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const path = require("path"),
+ os = require("os"),
+ ConfigOps = require("./config/config-ops"),
+ ConfigFile = require("./config/config-file"),
+ ConfigCache = require("./config/config-cache"),
+ Plugins = require("./config/plugins"),
+ FileFinder = require("./file-finder"),
+ isResolvable = require("is-resolvable");
+
+const debug = require("debug")("eslint:config");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const PERSONAL_CONFIG_DIR = os.homedir();
+const SUBCONFIG_SEP = ":";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Determines if any rules were explicitly passed in as options.
+ * @param {Object} options The options used to create our configuration.
+ * @returns {boolean} True if rules were passed in as options, false otherwise.
+ * @private
+ */
+function hasRules(options) {
+ return options.rules && Object.keys(options.rules).length > 0;
+}
+
+//------------------------------------------------------------------------------
+// API
+//------------------------------------------------------------------------------
+
+/**
+ * Configuration class
+ */
+class Config {
+
+ /**
+ * @param {Object} options Options to be passed in
+ * @param {Linter} linterContext Linter instance object
+ */
+ constructor(options, linterContext) {
+ options = options || {};
+
+ this.linterContext = linterContext;
+ this.plugins = new Plugins(linterContext.environments, linterContext.rules);
+
+ this.options = options;
+ this.ignore = options.ignore;
+ this.ignorePath = options.ignorePath;
+ this.parser = options.parser;
+ this.parserOptions = options.parserOptions || {};
+
+ this.configCache = new ConfigCache();
+
+ this.baseConfig = options.baseConfig
+ ? ConfigOps.merge({}, ConfigFile.loadObject(options.baseConfig, this))
+ : { rules: {} };
+ this.baseConfig.filePath = "";
+ this.baseConfig.baseDirectory = this.options.cwd;
+
+ this.configCache.setConfig(this.baseConfig.filePath, this.baseConfig);
+ this.configCache.setMergedVectorConfig(this.baseConfig.filePath, this.baseConfig);
+
+ this.useEslintrc = (options.useEslintrc !== false);
+
+ this.env = (options.envs || []).reduce((envs, name) => {
+ envs[name] = true;
+ return envs;
+ }, {});
+
+ /*
+ * Handle declared globals.
+ * For global variable foo, handle "foo:false" and "foo:true" to set
+ * whether global is writable.
+ * If user declares "foo", convert to "foo:false".
+ */
+ this.globals = (options.globals || []).reduce((globals, def) => {
+ const parts = def.split(SUBCONFIG_SEP);
+
+ globals[parts[0]] = (parts.length > 1 && parts[1] === "true");
+
+ return globals;
+ }, {});
+
+ this.loadSpecificConfig(options.configFile);
+
+ // Empty values in configs don't merge properly
+ const cliConfigOptions = {
+ env: this.env,
+ rules: this.options.rules,
+ globals: this.globals,
+ parserOptions: this.parserOptions,
+ plugins: this.options.plugins
+ };
+
+ this.cliConfig = {};
+ Object.keys(cliConfigOptions).forEach(configKey => {
+ const value = cliConfigOptions[configKey];
+
+ if (value) {
+ this.cliConfig[configKey] = value;
+ }
+ });
+ }
+
+ /**
+ * Loads the config options from a config specified on the command line.
+ * @param {string} [config] A shareable named config or path to a config file.
+ * @returns {void}
+ */
+ loadSpecificConfig(config) {
+ if (config) {
+ debug(`Using command line config ${config}`);
+ const isNamedConfig =
+ isResolvable(config) ||
+ isResolvable(`eslint-config-${config}`) ||
+ config.charAt(0) === "@";
+
+ if (!isNamedConfig) {
+ config = path.resolve(this.options.cwd, config);
+ }
+
+ this.specificConfig = ConfigFile.load(config, this);
+ }
+ }
+
+ /**
+ * Gets the personal config object from user's home directory.
+ * @returns {Object} the personal config object (null if there is no personal config)
+ * @private
+ */
+ getPersonalConfig() {
+ if (typeof this.personalConfig === "undefined") {
+ let config;
+ const filename = ConfigFile.getFilenameForDirectory(PERSONAL_CONFIG_DIR);
+
+ if (filename) {
+ debug("Using personal config");
+ config = ConfigFile.load(filename, this);
+ }
+
+ this.personalConfig = config || null;
+ }
+
+ return this.personalConfig;
+ }
+
+ /**
+ * Builds a hierarchy of config objects, including the base config, all local configs from the directory tree,
+ * and a config file specified on the command line, if applicable.
+ * @param {string} directory a file in whose directory we start looking for a local config
+ * @returns {Object[]} The config objects, in ascending order of precedence
+ * @private
+ */
+ getConfigHierarchy(directory) {
+ debug(`Constructing config file hierarchy for ${directory}`);
+
+ // Step 1: Always include baseConfig
+ let configs = [this.baseConfig];
+
+ // Step 2: Add user-specified config from .eslintrc.* and package.json files
+ if (this.useEslintrc) {
+ debug("Using .eslintrc and package.json files");
+ configs = configs.concat(this.getLocalConfigHierarchy(directory));
+ } else {
+ debug("Not using .eslintrc or package.json files");
+ }
+
+ // Step 3: Merge in command line config file
+ if (this.specificConfig) {
+ debug("Using command line config file");
+ configs.push(this.specificConfig);
+ }
+
+ return configs;
+ }
+
+ /**
+ * Gets a list of config objects extracted from local config files that apply to the current directory, in
+ * descending order, beginning with the config that is highest in the directory tree.
+ * @param {string} directory The directory to start looking in for local config files.
+ * @returns {Object[]} The shallow local config objects, in ascending order of precedence (closest to the current
+ * directory at the end), or an empty array if there are no local configs.
+ * @private
+ */
+ getLocalConfigHierarchy(directory) {
+ const localConfigFiles = this.findLocalConfigFiles(directory),
+ projectConfigPath = ConfigFile.getFilenameForDirectory(this.options.cwd),
+ searched = [],
+ configs = [];
+
+ for (const localConfigFile of localConfigFiles) {
+ const localConfigDirectory = path.dirname(localConfigFile);
+ const localConfigHierarchyCache = this.configCache.getHierarchyLocalConfigs(localConfigDirectory);
+
+ if (localConfigHierarchyCache) {
+ const localConfigHierarchy = localConfigHierarchyCache.concat(configs.reverse());
+
+ this.configCache.setHierarchyLocalConfigs(searched, localConfigHierarchy);
+ return localConfigHierarchy;
+ }
+
+ /*
+ * Don't consider the personal config file in the home directory,
+ * except if the home directory is the same as the current working directory
+ */
+ if (localConfigDirectory === PERSONAL_CONFIG_DIR && localConfigFile !== projectConfigPath) {
+ continue;
+ }
+
+ debug(`Loading ${localConfigFile}`);
+ const localConfig = ConfigFile.load(localConfigFile, this);
+
+ // Ignore empty config files
+ if (!localConfig) {
+ continue;
+ }
+
+ debug(`Using ${localConfigFile}`);
+ configs.push(localConfig);
+ searched.push(localConfigDirectory);
+
+ // Stop traversing if a config is found with the root flag set
+ if (localConfig.root) {
+ break;
+ }
+ }
+
+ if (!configs.length && !this.specificConfig) {
+
+ // Fall back on the personal config from ~/.eslintrc
+ debug("Using personal config file");
+ const personalConfig = this.getPersonalConfig();
+
+ if (personalConfig) {
+ configs.push(personalConfig);
+ } else if (!hasRules(this.options) && !this.options.baseConfig) {
+
+ // No config file, no manual configuration, and no rules, so error.
+ const noConfigError = new Error("No ESLint configuration found.");
+
+ noConfigError.messageTemplate = "no-config-found";
+ noConfigError.messageData = {
+ directory,
+ filesExamined: localConfigFiles
+ };
+
+ throw noConfigError;
+ }
+ }
+
+ // Set the caches for the parent directories
+ this.configCache.setHierarchyLocalConfigs(searched, configs.reverse());
+
+ return configs;
+ }
+
+ /**
+ * Gets the vector of applicable configs and subconfigs from the hierarchy for a given file. A vector is an array of
+ * entries, each of which in an object specifying a config file path and an array of override indices corresponding
+ * to entries in the config file's overrides section whose glob patterns match the specified file path; e.g., the
+ * vector entry { configFile: '/home/john/app/.eslintrc', matchingOverrides: [0, 2] } would indicate that the main
+ * project .eslintrc file and its first and third override blocks apply to the current file.
+ * @param {string} filePath The file path for which to build the hierarchy and config vector.
+ * @returns {Array<Object>} config vector applicable to the specified path
+ * @private
+ */
+ getConfigVector(filePath) {
+ const directory = filePath ? path.dirname(filePath) : this.options.cwd;
+
+ return this.getConfigHierarchy(directory).map(config => {
+ const vectorEntry = {
+ filePath: config.filePath,
+ matchingOverrides: []
+ };
+
+ if (config.overrides) {
+ const relativePath = path.relative(config.baseDirectory, filePath || directory);
+
+ config.overrides.forEach((override, i) => {
+ if (ConfigOps.pathMatchesGlobs(relativePath, override.files, override.excludedFiles)) {
+ vectorEntry.matchingOverrides.push(i);
+ }
+ });
+ }
+
+ return vectorEntry;
+ });
+ }
+
+ /**
+ * Finds local config files from the specified directory and its parent directories.
+ * @param {string} directory The directory to start searching from.
+ * @returns {GeneratorFunction} The paths of local config files found.
+ */
+ findLocalConfigFiles(directory) {
+ if (!this.localConfigFinder) {
+ this.localConfigFinder = new FileFinder(ConfigFile.CONFIG_FILES, this.options.cwd);
+ }
+
+ return this.localConfigFinder.findAllInDirectoryAndParents(directory);
+ }
+
+ /**
+ * Builds the authoritative config object for the specified file path by merging the hierarchy of config objects
+ * that apply to the current file, including the base config (conf/eslint-recommended), the user's personal config
+ * from their homedir, all local configs from the directory tree, any specific config file passed on the command
+ * line, any configuration overrides set directly on the command line, and finally the environment configs
+ * (conf/environments).
+ * @param {string} filePath a file in whose directory we start looking for a local config
+ * @returns {Object} config object
+ */
+ getConfig(filePath) {
+ const vector = this.getConfigVector(filePath);
+ let config = this.configCache.getMergedConfig(vector);
+
+ if (config) {
+ debug("Using config from cache");
+ return config;
+ }
+
+ // Step 1: Merge in the filesystem configurations (base, local, and personal)
+ config = ConfigOps.getConfigFromVector(vector, this.configCache);
+
+ // Step 2: Merge in command line configurations
+ config = ConfigOps.merge(config, this.cliConfig);
+
+ if (this.cliConfig.plugins) {
+ this.plugins.loadAll(this.cliConfig.plugins);
+ }
+
+ /*
+ * Step 3: Override parser only if it is passed explicitly through the command line
+ * or if it's not defined yet (because the final object will at least have the parser key)
+ */
+ if (this.parser || !config.parser) {
+ config = ConfigOps.merge(config, { parser: this.parser });
+ }
+
+ // Step 4: Apply environments to the config
+ config = ConfigOps.applyEnvironments(config, this.linterContext.environments);
+
+ this.configCache.setMergedConfig(vector, config);
+
+ return config;
+ }
+}
+
+module.exports = Config;
diff --git a/tools/node_modules/eslint/lib/config/autoconfig.js b/tools/node_modules/eslint/lib/config/autoconfig.js
new file mode 100644
index 0000000000..8536fdc55a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/autoconfig.js
@@ -0,0 +1,359 @@
+/**
+ * @fileoverview Used for creating a suggested configuration based on project code.
+ * @author Ian VanSchooten
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash"),
+ Linter = require("../linter"),
+ configRule = require("./config-rule"),
+ ConfigOps = require("./config-ops"),
+ recConfig = require("../../conf/eslint-recommended");
+
+const debug = require("debug")("eslint:autoconfig");
+const linter = new Linter();
+
+//------------------------------------------------------------------------------
+// Data
+//------------------------------------------------------------------------------
+
+const MAX_CONFIG_COMBINATIONS = 17, // 16 combinations + 1 for severity only
+ RECOMMENDED_CONFIG_NAME = "eslint:recommended";
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Information about a rule configuration, in the context of a Registry.
+ *
+ * @typedef {Object} registryItem
+ * @param {ruleConfig} config A valid configuration for the rule
+ * @param {number} specificity The number of elements in the ruleConfig array
+ * @param {number} errorCount The number of errors encountered when linting with the config
+ */
+
+/**
+ * This callback is used to measure execution status in a progress bar
+ * @callback progressCallback
+ * @param {number} The total number of times the callback will be called.
+ */
+
+/**
+ * Create registryItems for rules
+ * @param {rulesConfig} rulesConfig Hash of rule names and arrays of ruleConfig items
+ * @returns {Object} registryItems for each rule in provided rulesConfig
+ */
+function makeRegistryItems(rulesConfig) {
+ return Object.keys(rulesConfig).reduce((accumulator, ruleId) => {
+ accumulator[ruleId] = rulesConfig[ruleId].map(config => ({
+ config,
+ specificity: config.length || 1,
+ errorCount: void 0
+ }));
+ return accumulator;
+ }, {});
+}
+
+/**
+ * Creates an object in which to store rule configs and error counts
+ *
+ * Unless a rulesConfig is provided at construction, the registry will not contain
+ * any rules, only methods. This will be useful for building up registries manually.
+ *
+ * Registry class
+ */
+class Registry {
+
+ /**
+ * @param {rulesConfig} [rulesConfig] Hash of rule names and arrays of possible configurations
+ */
+ constructor(rulesConfig) {
+ this.rules = (rulesConfig) ? makeRegistryItems(rulesConfig) : {};
+ }
+
+ /**
+ * Populate the registry with core rule configs.
+ *
+ * It will set the registry's `rule` property to an object having rule names
+ * as keys and an array of registryItems as values.
+ *
+ * @returns {void}
+ */
+ populateFromCoreRules() {
+ const rulesConfig = configRule.createCoreRuleConfigs();
+
+ this.rules = makeRegistryItems(rulesConfig);
+ }
+
+ /**
+ * Creates sets of rule configurations which can be used for linting
+ * and initializes registry errors to zero for those configurations (side effect).
+ *
+ * This combines as many rules together as possible, such that the first sets
+ * in the array will have the highest number of rules configured, and later sets
+ * will have fewer and fewer, as not all rules have the same number of possible
+ * configurations.
+ *
+ * The length of the returned array will be <= MAX_CONFIG_COMBINATIONS.
+ *
+ * @param {Object} registry The autoconfig registry
+ * @returns {Object[]} "rules" configurations to use for linting
+ */
+ buildRuleSets() {
+ let idx = 0;
+ const ruleIds = Object.keys(this.rules),
+ ruleSets = [];
+
+ /**
+ * Add a rule configuration from the registry to the ruleSets
+ *
+ * This is broken out into its own function so that it doesn't need to be
+ * created inside of the while loop.
+ *
+ * @param {string} rule The ruleId to add.
+ * @returns {void}
+ */
+ const addRuleToRuleSet = function(rule) {
+
+ /*
+ * This check ensures that there is a rule configuration and that
+ * it has fewer than the max combinations allowed.
+ * If it has too many configs, we will only use the most basic of
+ * the possible configurations.
+ */
+ const hasFewCombos = (this.rules[rule].length <= MAX_CONFIG_COMBINATIONS);
+
+ if (this.rules[rule][idx] && (hasFewCombos || this.rules[rule][idx].specificity <= 2)) {
+
+ /*
+ * If the rule has too many possible combinations, only take
+ * simple ones, avoiding objects.
+ */
+ if (!hasFewCombos && typeof this.rules[rule][idx].config[1] === "object") {
+ return;
+ }
+
+ ruleSets[idx] = ruleSets[idx] || {};
+ ruleSets[idx][rule] = this.rules[rule][idx].config;
+
+ /*
+ * Initialize errorCount to zero, since this is a config which
+ * will be linted.
+ */
+ this.rules[rule][idx].errorCount = 0;
+ }
+ }.bind(this);
+
+ while (ruleSets.length === idx) {
+ ruleIds.forEach(addRuleToRuleSet);
+ idx += 1;
+ }
+
+ return ruleSets;
+ }
+
+ /**
+ * Remove all items from the registry with a non-zero number of errors
+ *
+ * Note: this also removes rule configurations which were not linted
+ * (meaning, they have an undefined errorCount).
+ *
+ * @returns {void}
+ */
+ stripFailingConfigs() {
+ const ruleIds = Object.keys(this.rules),
+ newRegistry = new Registry();
+
+ newRegistry.rules = Object.assign({}, this.rules);
+ ruleIds.forEach(ruleId => {
+ const errorFreeItems = newRegistry.rules[ruleId].filter(registryItem => (registryItem.errorCount === 0));
+
+ if (errorFreeItems.length > 0) {
+ newRegistry.rules[ruleId] = errorFreeItems;
+ } else {
+ delete newRegistry.rules[ruleId];
+ }
+ });
+
+ return newRegistry;
+ }
+
+ /**
+ * Removes rule configurations which were not included in a ruleSet
+ *
+ * @returns {void}
+ */
+ stripExtraConfigs() {
+ const ruleIds = Object.keys(this.rules),
+ newRegistry = new Registry();
+
+ newRegistry.rules = Object.assign({}, this.rules);
+ ruleIds.forEach(ruleId => {
+ newRegistry.rules[ruleId] = newRegistry.rules[ruleId].filter(registryItem => (typeof registryItem.errorCount !== "undefined"));
+ });
+
+ return newRegistry;
+ }
+
+ /**
+ * Creates a registry of rules which had no error-free configs.
+ * The new registry is intended to be analyzed to determine whether its rules
+ * should be disabled or set to warning.
+ *
+ * @returns {Registry} A registry of failing rules.
+ */
+ getFailingRulesRegistry() {
+ const ruleIds = Object.keys(this.rules),
+ failingRegistry = new Registry();
+
+ ruleIds.forEach(ruleId => {
+ const failingConfigs = this.rules[ruleId].filter(registryItem => (registryItem.errorCount > 0));
+
+ if (failingConfigs && failingConfigs.length === this.rules[ruleId].length) {
+ failingRegistry.rules[ruleId] = failingConfigs;
+ }
+ });
+
+ return failingRegistry;
+ }
+
+ /**
+ * Create an eslint config for any rules which only have one configuration
+ * in the registry.
+ *
+ * @returns {Object} An eslint config with rules section populated
+ */
+ createConfig() {
+ const ruleIds = Object.keys(this.rules),
+ config = { rules: {} };
+
+ ruleIds.forEach(ruleId => {
+ if (this.rules[ruleId].length === 1) {
+ config.rules[ruleId] = this.rules[ruleId][0].config;
+ }
+ });
+
+ return config;
+ }
+
+ /**
+ * Return a cloned registry containing only configs with a desired specificity
+ *
+ * @param {number} specificity Only keep configs with this specificity
+ * @returns {Registry} A registry of rules
+ */
+ filterBySpecificity(specificity) {
+ const ruleIds = Object.keys(this.rules),
+ newRegistry = new Registry();
+
+ newRegistry.rules = Object.assign({}, this.rules);
+ ruleIds.forEach(ruleId => {
+ newRegistry.rules[ruleId] = this.rules[ruleId].filter(registryItem => (registryItem.specificity === specificity));
+ });
+
+ return newRegistry;
+ }
+
+ /**
+ * Lint SourceCodes against all configurations in the registry, and record results
+ *
+ * @param {Object[]} sourceCodes SourceCode objects for each filename
+ * @param {Object} config ESLint config object
+ * @param {progressCallback} [cb] Optional callback for reporting execution status
+ * @returns {Registry} New registry with errorCount populated
+ */
+ lintSourceCode(sourceCodes, config, cb) {
+ let lintedRegistry = new Registry();
+
+ lintedRegistry.rules = Object.assign({}, this.rules);
+
+ const ruleSets = lintedRegistry.buildRuleSets();
+
+ lintedRegistry = lintedRegistry.stripExtraConfigs();
+
+ debug("Linting with all possible rule combinations");
+
+ const filenames = Object.keys(sourceCodes);
+ const totalFilesLinting = filenames.length * ruleSets.length;
+
+ filenames.forEach(filename => {
+ debug(`Linting file: ${filename}`);
+
+ let ruleSetIdx = 0;
+
+ ruleSets.forEach(ruleSet => {
+ const lintConfig = Object.assign({}, config, { rules: ruleSet });
+ const lintResults = linter.verify(sourceCodes[filename], lintConfig);
+
+ lintResults.forEach(result => {
+
+ /*
+ * It is possible that the error is from a configuration comment
+ * in a linted file, in which case there may not be a config
+ * set in this ruleSetIdx.
+ * (https://github.com/eslint/eslint/issues/5992)
+ * (https://github.com/eslint/eslint/issues/7860)
+ */
+ if (
+ lintedRegistry.rules[result.ruleId] &&
+ lintedRegistry.rules[result.ruleId][ruleSetIdx]
+ ) {
+ lintedRegistry.rules[result.ruleId][ruleSetIdx].errorCount += 1;
+ }
+ });
+
+ ruleSetIdx += 1;
+
+ if (cb) {
+ cb(totalFilesLinting); // eslint-disable-line callback-return
+ }
+ });
+
+ // Deallocate for GC
+ sourceCodes[filename] = null;
+ });
+
+ return lintedRegistry;
+ }
+}
+
+/**
+ * Extract rule configuration into eslint:recommended where possible.
+ *
+ * This will return a new config with `"extends": "eslint:recommended"` and
+ * only the rules which have configurations different from the recommended config.
+ *
+ * @param {Object} config config object
+ * @returns {Object} config object using `"extends": "eslint:recommended"`
+ */
+function extendFromRecommended(config) {
+ const newConfig = Object.assign({}, config);
+
+ ConfigOps.normalizeToStrings(newConfig);
+
+ const recRules = Object.keys(recConfig.rules).filter(ruleId => ConfigOps.isErrorSeverity(recConfig.rules[ruleId]));
+
+ recRules.forEach(ruleId => {
+ if (lodash.isEqual(recConfig.rules[ruleId], newConfig.rules[ruleId])) {
+ delete newConfig.rules[ruleId];
+ }
+ });
+ newConfig.extends = RECOMMENDED_CONFIG_NAME;
+ return newConfig;
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ Registry,
+ extendFromRecommended
+};
diff --git a/tools/node_modules/eslint/lib/config/config-cache.js b/tools/node_modules/eslint/lib/config/config-cache.js
new file mode 100644
index 0000000000..07436a87c8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-cache.js
@@ -0,0 +1,130 @@
+/**
+ * @fileoverview Responsible for caching config files
+ * @author Sylvan Mably
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Get a string hash for a config vector
+ * @param {Array<Object>} vector config vector to hash
+ * @returns {string} hash of the vector values
+ * @private
+ */
+function hash(vector) {
+ return JSON.stringify(vector);
+}
+
+//------------------------------------------------------------------------------
+// API
+//------------------------------------------------------------------------------
+
+/**
+ * Configuration caching class
+ */
+module.exports = class ConfigCache {
+
+ constructor() {
+ this.configFullNameCache = new Map();
+ this.localHierarchyCache = new Map();
+ this.mergedVectorCache = new Map();
+ this.mergedCache = new Map();
+ }
+
+ /**
+ * Gets a config object from the cache for the specified config file path.
+ * @param {string} configFullName the name of the configuration as used in the eslint config(e.g. 'plugin:node/recommended'),
+ * or the absolute path to a config file. This should uniquely identify a config.
+ * @returns {Object|null} config object, if found in the cache, otherwise null
+ * @private
+ */
+ getConfig(configFullName) {
+ return this.configFullNameCache.get(configFullName);
+ }
+
+ /**
+ * Sets a config object in the cache for the specified config file path.
+ * @param {string} configFullName the name of the configuration as used in the eslint config(e.g. 'plugin:node/recommended'),
+ * or the absolute path to a config file. This should uniquely identify a config.
+ * @param {Object} config the config object to add to the cache
+ * @returns {void}
+ * @private
+ */
+ setConfig(configFullName, config) {
+ this.configFullNameCache.set(configFullName, config);
+ }
+
+ /**
+ * Gets a list of hierarchy-local config objects that apply to the specified directory.
+ * @param {string} directory the path to the directory
+ * @returns {Object[]|null} a list of config objects, if found in the cache, otherwise null
+ * @private
+ */
+ getHierarchyLocalConfigs(directory) {
+ return this.localHierarchyCache.get(directory);
+ }
+
+ /**
+ * For each of the supplied parent directories, sets the list of config objects for that directory to the
+ * appropriate subset of the supplied parent config objects.
+ * @param {string[]} parentDirectories a list of parent directories to add to the config cache
+ * @param {Object[]} parentConfigs a list of config objects that apply to the lowest directory in parentDirectories
+ * @returns {void}
+ * @private
+ */
+ setHierarchyLocalConfigs(parentDirectories, parentConfigs) {
+ parentDirectories.forEach((localConfigDirectory, i) => {
+ const directoryParentConfigs = parentConfigs.slice(0, parentConfigs.length - i);
+
+ this.localHierarchyCache.set(localConfigDirectory, directoryParentConfigs);
+ });
+ }
+
+ /**
+ * Gets a merged config object corresponding to the supplied vector.
+ * @param {Array<Object>} vector the vector to find a merged config for
+ * @returns {Object|null} a merged config object, if found in the cache, otherwise null
+ * @private
+ */
+ getMergedVectorConfig(vector) {
+ return this.mergedVectorCache.get(hash(vector));
+ }
+
+ /**
+ * Sets a merged config object in the cache for the supplied vector.
+ * @param {Array<Object>} vector the vector to save a merged config for
+ * @param {Object} config the merged config object to add to the cache
+ * @returns {void}
+ * @private
+ */
+ setMergedVectorConfig(vector, config) {
+ this.mergedVectorCache.set(hash(vector), config);
+ }
+
+ /**
+ * Gets a merged config object corresponding to the supplied vector, including configuration options from outside
+ * the vector.
+ * @param {Array<Object>} vector the vector to find a merged config for
+ * @returns {Object|null} a merged config object, if found in the cache, otherwise null
+ * @private
+ */
+ getMergedConfig(vector) {
+ return this.mergedCache.get(hash(vector));
+ }
+
+ /**
+ * Sets a merged config object in the cache for the supplied vector, including configuration options from outside
+ * the vector.
+ * @param {Array<Object>} vector the vector to save a merged config for
+ * @param {Object} config the merged config object to add to the cache
+ * @returns {void}
+ * @private
+ */
+ setMergedConfig(vector, config) {
+ this.mergedCache.set(hash(vector), config);
+ }
+};
diff --git a/tools/node_modules/eslint/lib/config/config-file.js b/tools/node_modules/eslint/lib/config/config-file.js
new file mode 100644
index 0000000000..c5ff073cfc
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-file.js
@@ -0,0 +1,595 @@
+/**
+ * @fileoverview Helper to locate and load configuration files.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path"),
+ ConfigOps = require("./config-ops"),
+ validator = require("./config-validator"),
+ ModuleResolver = require("../util/module-resolver"),
+ naming = require("../util/naming"),
+ pathIsInside = require("path-is-inside"),
+ stripComments = require("strip-json-comments"),
+ stringify = require("json-stable-stringify-without-jsonify"),
+ requireUncached = require("require-uncached");
+
+const debug = require("debug")("eslint:config-file");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Determines sort order for object keys for json-stable-stringify
+ *
+ * see: https://github.com/samn/json-stable-stringify#cmp
+ *
+ * @param {Object} a The first comparison object ({key: akey, value: avalue})
+ * @param {Object} b The second comparison object ({key: bkey, value: bvalue})
+ * @returns {number} 1 or -1, used in stringify cmp method
+ */
+function sortByKey(a, b) {
+ return a.key > b.key ? 1 : -1;
+}
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const CONFIG_FILES = [
+ ".eslintrc.js",
+ ".eslintrc.yaml",
+ ".eslintrc.yml",
+ ".eslintrc.json",
+ ".eslintrc",
+ "package.json"
+];
+
+const resolver = new ModuleResolver();
+
+/**
+ * Convenience wrapper for synchronously reading file contents.
+ * @param {string} filePath The filename to read.
+ * @returns {string} The file contents, with the BOM removed.
+ * @private
+ */
+function readFile(filePath) {
+ return fs.readFileSync(filePath, "utf8").replace(/^\ufeff/, "");
+}
+
+/**
+ * Determines if a given string represents a filepath or not using the same
+ * conventions as require(), meaning that the first character must be nonalphanumeric
+ * and not the @ sign which is used for scoped packages to be considered a file path.
+ * @param {string} filePath The string to check.
+ * @returns {boolean} True if it's a filepath, false if not.
+ * @private
+ */
+function isFilePath(filePath) {
+ return path.isAbsolute(filePath) || !/\w|@/.test(filePath.charAt(0));
+}
+
+/**
+ * Loads a YAML configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadYAMLConfigFile(filePath) {
+ debug(`Loading YAML config file: ${filePath}`);
+
+ // lazy load YAML to improve performance when not used
+ const yaml = require("js-yaml");
+
+ try {
+
+ // empty YAML file can be null, so always use
+ return yaml.safeLoad(readFile(filePath)) || {};
+ } catch (e) {
+ debug(`Error reading YAML file: ${filePath}`);
+ e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
+ throw e;
+ }
+}
+
+/**
+ * Loads a JSON configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadJSONConfigFile(filePath) {
+ debug(`Loading JSON config file: ${filePath}`);
+
+ try {
+ return JSON.parse(stripComments(readFile(filePath)));
+ } catch (e) {
+ debug(`Error reading JSON file: ${filePath}`);
+ e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
+ throw e;
+ }
+}
+
+/**
+ * Loads a legacy (.eslintrc) configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadLegacyConfigFile(filePath) {
+ debug(`Loading config file: ${filePath}`);
+
+ // lazy load YAML to improve performance when not used
+ const yaml = require("js-yaml");
+
+ try {
+ return yaml.safeLoad(stripComments(readFile(filePath))) || /* istanbul ignore next */ {};
+ } catch (e) {
+ debug(`Error reading YAML file: ${filePath}`);
+ e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
+ throw e;
+ }
+}
+
+/**
+ * Loads a JavaScript configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadJSConfigFile(filePath) {
+ debug(`Loading JS config file: ${filePath}`);
+ try {
+ return requireUncached(filePath);
+ } catch (e) {
+ debug(`Error reading JavaScript file: ${filePath}`);
+ e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
+ throw e;
+ }
+}
+
+/**
+ * Loads a configuration from a package.json file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadPackageJSONConfigFile(filePath) {
+ debug(`Loading package.json config file: ${filePath}`);
+ try {
+ return loadJSONConfigFile(filePath).eslintConfig || null;
+ } catch (e) {
+ debug(`Error reading package.json file: ${filePath}`);
+ e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
+ throw e;
+ }
+}
+
+/**
+ * Creates an error to notify about a missing config to extend from.
+ * @param {string} configName The name of the missing config.
+ * @returns {Error} The error object to throw
+ * @private
+ */
+function configMissingError(configName) {
+ const error = new Error(`Failed to load config "${configName}" to extend from.`);
+
+ error.messageTemplate = "extend-config-missing";
+ error.messageData = {
+ configName
+ };
+ return error;
+}
+
+/**
+ * Loads a configuration file regardless of the source. Inspects the file path
+ * to determine the correctly way to load the config file.
+ * @param {Object} file The path to the configuration.
+ * @returns {Object} The configuration information.
+ * @private
+ */
+function loadConfigFile(file) {
+ const filePath = file.filePath;
+ let config;
+
+ switch (path.extname(filePath)) {
+ case ".js":
+ config = loadJSConfigFile(filePath);
+ if (file.configName) {
+ config = config.configs[file.configName];
+ if (!config) {
+ throw configMissingError(file.configFullName);
+ }
+ }
+ break;
+
+ case ".json":
+ if (path.basename(filePath) === "package.json") {
+ config = loadPackageJSONConfigFile(filePath);
+ if (config === null) {
+ return null;
+ }
+ } else {
+ config = loadJSONConfigFile(filePath);
+ }
+ break;
+
+ case ".yaml":
+ case ".yml":
+ config = loadYAMLConfigFile(filePath);
+ break;
+
+ default:
+ config = loadLegacyConfigFile(filePath);
+ }
+
+ return ConfigOps.merge(ConfigOps.createEmptyConfig(), config);
+}
+
+/**
+ * Writes a configuration file in JSON format.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @private
+ */
+function writeJSONConfigFile(config, filePath) {
+ debug(`Writing JSON config file: ${filePath}`);
+
+ const content = stringify(config, { cmp: sortByKey, space: 4 });
+
+ fs.writeFileSync(filePath, content, "utf8");
+}
+
+/**
+ * Writes a configuration file in YAML format.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @private
+ */
+function writeYAMLConfigFile(config, filePath) {
+ debug(`Writing YAML config file: ${filePath}`);
+
+ // lazy load YAML to improve performance when not used
+ const yaml = require("js-yaml");
+
+ const content = yaml.safeDump(config, { sortKeys: true });
+
+ fs.writeFileSync(filePath, content, "utf8");
+}
+
+/**
+ * Writes a configuration file in JavaScript format.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @private
+ */
+function writeJSConfigFile(config, filePath) {
+ debug(`Writing JS config file: ${filePath}`);
+
+ const content = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })};`;
+
+ fs.writeFileSync(filePath, content, "utf8");
+}
+
+/**
+ * Writes a configuration file.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @throws {Error} When an unknown file type is specified.
+ * @private
+ */
+function write(config, filePath) {
+ switch (path.extname(filePath)) {
+ case ".js":
+ writeJSConfigFile(config, filePath);
+ break;
+
+ case ".json":
+ writeJSONConfigFile(config, filePath);
+ break;
+
+ case ".yaml":
+ case ".yml":
+ writeYAMLConfigFile(config, filePath);
+ break;
+
+ default:
+ throw new Error("Can't write to unknown file type.");
+ }
+}
+
+/**
+ * Determines the base directory for node packages referenced in a config file.
+ * This does not include node_modules in the path so it can be used for all
+ * references relative to a config file.
+ * @param {string} configFilePath The config file referencing the file.
+ * @returns {string} The base directory for the file path.
+ * @private
+ */
+function getBaseDir(configFilePath) {
+
+ // calculates the path of the project including ESLint as dependency
+ const projectPath = path.resolve(__dirname, "../../../");
+
+ if (configFilePath && pathIsInside(configFilePath, projectPath)) {
+
+ // be careful of https://github.com/substack/node-resolve/issues/78
+ return path.join(path.resolve(configFilePath));
+ }
+
+ /*
+ * default to ESLint project path since it's unlikely that plugins will be
+ * in this directory
+ */
+ return path.join(projectPath);
+}
+
+/**
+ * Determines the lookup path, including node_modules, for package
+ * references relative to a config file.
+ * @param {string} configFilePath The config file referencing the file.
+ * @returns {string} The lookup path for the file path.
+ * @private
+ */
+function getLookupPath(configFilePath) {
+ const basedir = getBaseDir(configFilePath);
+
+ return path.join(basedir, "node_modules");
+}
+
+/**
+ * Resolves a eslint core config path
+ * @param {string} name The eslint config name.
+ * @returns {string} The resolved path of the config.
+ * @private
+ */
+function getEslintCoreConfigPath(name) {
+ if (name === "eslint:recommended") {
+
+ /*
+ * Add an explicit substitution for eslint:recommended to
+ * conf/eslint-recommended.js.
+ */
+ return path.resolve(__dirname, "../../conf/eslint-recommended.js");
+ }
+
+ if (name === "eslint:all") {
+
+ /*
+ * Add an explicit substitution for eslint:all to conf/eslint-all.js
+ */
+ return path.resolve(__dirname, "../../conf/eslint-all.js");
+ }
+
+ throw configMissingError(name);
+}
+
+/**
+ * Applies values from the "extends" field in a configuration file.
+ * @param {Object} config The configuration information.
+ * @param {Config} configContext Plugin context for the config instance
+ * @param {string} filePath The file path from which the configuration information
+ * was loaded.
+ * @param {string} [relativeTo] The path to resolve relative to.
+ * @returns {Object} A new configuration object with all of the "extends" fields
+ * loaded and merged.
+ * @private
+ */
+function applyExtends(config, configContext, filePath, relativeTo) {
+ let configExtends = config.extends;
+
+ // normalize into an array for easier handling
+ if (!Array.isArray(config.extends)) {
+ configExtends = [config.extends];
+ }
+
+ // Make the last element in an array take the highest precedence
+ config = configExtends.reduceRight((previousValue, parentPath) => {
+ try {
+ if (parentPath.startsWith("eslint:")) {
+ parentPath = getEslintCoreConfigPath(parentPath);
+ } else if (isFilePath(parentPath)) {
+
+ /*
+ * If the `extends` path is relative, use the directory of the current configuration
+ * file as the reference point. Otherwise, use as-is.
+ */
+ parentPath = (path.isAbsolute(parentPath)
+ ? parentPath
+ : path.join(relativeTo || path.dirname(filePath), parentPath)
+ );
+ }
+ debug(`Loading ${parentPath}`);
+
+ // eslint-disable-next-line no-use-before-define
+ return ConfigOps.merge(load(parentPath, configContext, relativeTo), previousValue);
+ } catch (e) {
+
+ /*
+ * If the file referenced by `extends` failed to load, add the path
+ * to the configuration file that referenced it to the error
+ * message so the user is able to see where it was referenced from,
+ * then re-throw.
+ */
+ e.message += `\nReferenced from: ${filePath}`;
+ throw e;
+ }
+
+ }, config);
+
+ return config;
+}
+
+/**
+ * Resolves a configuration file path into the fully-formed path, whether filename
+ * or package name.
+ * @param {string} filePath The filepath to resolve.
+ * @param {string} [relativeTo] The path to resolve relative to.
+ * @returns {Object} An object containing 3 properties:
+ * - 'filePath' (required) the resolved path that can be used directly to load the configuration.
+ * - 'configName' the name of the configuration inside the plugin.
+ * - 'configFullName' (required) the name of the configuration as used in the eslint config(e.g. 'plugin:node/recommended'),
+ * or the absolute path to a config file. This should uniquely identify a config.
+ * @private
+ */
+function resolve(filePath, relativeTo) {
+ if (isFilePath(filePath)) {
+ const fullPath = path.resolve(relativeTo || "", filePath);
+
+ return { filePath: fullPath, configFullName: fullPath };
+ }
+ let normalizedPackageName;
+
+ if (filePath.startsWith("plugin:")) {
+ const configFullName = filePath;
+ const pluginName = filePath.slice(7, filePath.lastIndexOf("/"));
+ const configName = filePath.slice(filePath.lastIndexOf("/") + 1);
+
+ normalizedPackageName = naming.normalizePackageName(pluginName, "eslint-plugin");
+ debug(`Attempting to resolve ${normalizedPackageName}`);
+ filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
+ return { filePath, configName, configFullName };
+ }
+ normalizedPackageName = naming.normalizePackageName(filePath, "eslint-config");
+ debug(`Attempting to resolve ${normalizedPackageName}`);
+ filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
+ return { filePath, configFullName: filePath };
+
+
+}
+
+/**
+ * Loads a configuration file from the given file path.
+ * @param {Object} resolvedPath The value from calling resolve() on a filename or package name.
+ * @param {Config} configContext Plugins context
+ * @returns {Object} The configuration information.
+ */
+function loadFromDisk(resolvedPath, configContext) {
+ const dirname = path.dirname(resolvedPath.filePath),
+ lookupPath = getLookupPath(dirname);
+ let config = loadConfigFile(resolvedPath);
+
+ if (config) {
+
+ // ensure plugins are properly loaded first
+ if (config.plugins) {
+ configContext.plugins.loadAll(config.plugins);
+ }
+
+ // include full path of parser if present
+ if (config.parser) {
+ if (isFilePath(config.parser)) {
+ config.parser = path.resolve(dirname || "", config.parser);
+ } else {
+ config.parser = resolver.resolve(config.parser, lookupPath);
+ }
+ }
+
+ const ruleMap = configContext.linterContext.getRules();
+
+ // validate the configuration before continuing
+ validator.validate(config, resolvedPath.configFullName, ruleMap.get.bind(ruleMap), configContext.linterContext.environments);
+
+ /*
+ * If an `extends` property is defined, it represents a configuration file to use as
+ * a "parent". Load the referenced file and merge the configuration recursively.
+ */
+ if (config.extends) {
+ config = applyExtends(config, configContext, resolvedPath.filePath, dirname);
+ }
+ }
+
+ return config;
+}
+
+/**
+ * Loads a config object, applying extends if present.
+ * @param {Object} configObject a config object to load
+ * @param {Config} configContext Context for the config instance
+ * @returns {Object} the config object with extends applied if present, or the passed config if not
+ * @private
+ */
+function loadObject(configObject, configContext) {
+ return configObject.extends ? applyExtends(configObject, configContext, "") : configObject;
+}
+
+/**
+ * Loads a config object from the config cache based on its filename, falling back to the disk if the file is not yet
+ * cached.
+ * @param {string} filePath the path to the config file
+ * @param {Config} configContext Context for the config instance
+ * @param {string} [relativeTo] The path to resolve relative to.
+ * @returns {Object} the parsed config object (empty object if there was a parse error)
+ * @private
+ */
+function load(filePath, configContext, relativeTo) {
+ const resolvedPath = resolve(filePath, relativeTo);
+
+ const cachedConfig = configContext.configCache.getConfig(resolvedPath.configFullName);
+
+ if (cachedConfig) {
+ return cachedConfig;
+ }
+
+ const config = loadFromDisk(resolvedPath, configContext);
+
+ if (config) {
+ config.filePath = resolvedPath.filePath;
+ config.baseDirectory = path.dirname(resolvedPath.filePath);
+ configContext.configCache.setConfig(resolvedPath.configFullName, config);
+ }
+
+ return config;
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ getBaseDir,
+ getLookupPath,
+ load,
+ loadObject,
+ resolve,
+ write,
+ applyExtends,
+ CONFIG_FILES,
+
+ /**
+ * Retrieves the configuration filename for a given directory. It loops over all
+ * of the valid configuration filenames in order to find the first one that exists.
+ * @param {string} directory The directory to check for a config file.
+ * @returns {?string} The filename of the configuration file for the directory
+ * or null if there is no configuration file in the directory.
+ */
+ getFilenameForDirectory(directory) {
+ for (let i = 0, len = CONFIG_FILES.length; i < len; i++) {
+ const filename = path.join(directory, CONFIG_FILES[i]);
+
+ if (fs.existsSync(filename) && fs.statSync(filename).isFile()) {
+ return filename;
+ }
+ }
+
+ return null;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/config/config-initializer.js b/tools/node_modules/eslint/lib/config/config-initializer.js
new file mode 100644
index 0000000000..e4865a008c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-initializer.js
@@ -0,0 +1,605 @@
+/**
+ * @fileoverview Config initialization wizard.
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const util = require("util"),
+ inquirer = require("inquirer"),
+ ProgressBar = require("progress"),
+ semver = require("semver"),
+ autoconfig = require("./autoconfig.js"),
+ ConfigFile = require("./config-file"),
+ ConfigOps = require("./config-ops"),
+ getSourceCodeOfFiles = require("../util/source-code-util").getSourceCodeOfFiles,
+ ModuleResolver = require("../util/module-resolver"),
+ npmUtil = require("../util/npm-util"),
+ recConfig = require("../../conf/eslint-recommended"),
+ log = require("../logging");
+
+const debug = require("debug")("eslint:config-initializer");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/* istanbul ignore next: hard to test fs function */
+/**
+ * Create .eslintrc file in the current working directory
+ * @param {Object} config object that contains user's answers
+ * @param {string} format The file format to write to.
+ * @returns {void}
+ */
+function writeFile(config, format) {
+
+ // default is .js
+ let extname = ".js";
+
+ if (format === "YAML") {
+ extname = ".yml";
+ } else if (format === "JSON") {
+ extname = ".json";
+ }
+
+ const installedESLint = config.installedESLint;
+
+ delete config.installedESLint;
+
+ ConfigFile.write(config, `./.eslintrc${extname}`);
+ log.info(`Successfully created .eslintrc${extname} file in ${process.cwd()}`);
+
+ if (installedESLint) {
+ log.info("ESLint was installed locally. We recommend using this local copy instead of your globally-installed copy.");
+ }
+}
+
+/**
+ * Get the peer dependencies of the given module.
+ * This adds the gotten value to cache at the first time, then reuses it.
+ * In a process, this function is called twice, but `npmUtil.fetchPeerDependencies` needs to access network which is relatively slow.
+ * @param {string} moduleName The module name to get.
+ * @returns {Object} The peer dependencies of the given module.
+ * This object is the object of `peerDependencies` field of `package.json`.
+ * Returns null if npm was not found.
+ */
+function getPeerDependencies(moduleName) {
+ let result = getPeerDependencies.cache.get(moduleName);
+
+ if (!result) {
+ log.info(`Checking peerDependencies of ${moduleName}`);
+
+ result = npmUtil.fetchPeerDependencies(moduleName);
+ getPeerDependencies.cache.set(moduleName, result);
+ }
+
+ return result;
+}
+getPeerDependencies.cache = new Map();
+
+/**
+ * Synchronously install necessary plugins, configs, parsers, etc. based on the config
+ * @param {Object} config config object
+ * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
+ * @returns {void}
+ */
+function installModules(config, installESLint) {
+ const modules = {};
+
+ // Create a list of modules which should be installed based on config
+ if (config.plugins) {
+ for (const plugin of config.plugins) {
+ modules[`eslint-plugin-${plugin}`] = "latest";
+ }
+ }
+ if (config.extends && config.extends.indexOf("eslint:") === -1) {
+ const moduleName = `eslint-config-${config.extends}`;
+
+ modules[moduleName] = "latest";
+ Object.assign(
+ modules,
+ getPeerDependencies(`${moduleName}@latest`)
+ );
+ }
+
+ // If no modules, do nothing.
+ if (Object.keys(modules).length === 0) {
+ return;
+ }
+
+ if (installESLint === false) {
+ delete modules.eslint;
+ } else {
+ const installStatus = npmUtil.checkDevDeps(["eslint"]);
+
+ // Mark to show messages if it's new installation of eslint.
+ if (installStatus.eslint === false) {
+ log.info("Local ESLint installation not found.");
+ modules.eslint = modules.eslint || "latest";
+ config.installedESLint = true;
+ }
+ }
+
+ // Install packages
+ const modulesToInstall = Object.keys(modules).map(name => `${name}@${modules[name]}`);
+
+ log.info(`Installing ${modulesToInstall.join(", ")}`);
+
+ npmUtil.installSyncSaveDev(modulesToInstall);
+}
+
+/**
+ * Set the `rules` of a config by examining a user's source code
+ *
+ * Note: This clones the config object and returns a new config to avoid mutating
+ * the original config parameter.
+ *
+ * @param {Object} answers answers received from inquirer
+ * @param {Object} config config object
+ * @returns {Object} config object with configured rules
+ */
+function configureRules(answers, config) {
+ const BAR_TOTAL = 20,
+ BAR_SOURCE_CODE_TOTAL = 4,
+ newConfig = Object.assign({}, config),
+ disabledConfigs = {};
+ let sourceCodes,
+ registry;
+
+ // Set up a progress bar, as this process can take a long time
+ const bar = new ProgressBar("Determining Config: :percent [:bar] :elapseds elapsed, eta :etas ", {
+ width: 30,
+ total: BAR_TOTAL
+ });
+
+ bar.tick(0); // Shows the progress bar
+
+ // Get the SourceCode of all chosen files
+ const patterns = answers.patterns.split(/[\s]+/);
+
+ try {
+ sourceCodes = getSourceCodeOfFiles(patterns, { baseConfig: newConfig, useEslintrc: false }, total => {
+ bar.tick((BAR_SOURCE_CODE_TOTAL / total));
+ });
+ } catch (e) {
+ log.info("\n");
+ throw e;
+ }
+ const fileQty = Object.keys(sourceCodes).length;
+
+ if (fileQty === 0) {
+ log.info("\n");
+ throw new Error("Automatic Configuration failed. No files were able to be parsed.");
+ }
+
+ // Create a registry of rule configs
+ registry = new autoconfig.Registry();
+ registry.populateFromCoreRules();
+
+ // Lint all files with each rule config in the registry
+ registry = registry.lintSourceCode(sourceCodes, newConfig, total => {
+ bar.tick((BAR_TOTAL - BAR_SOURCE_CODE_TOTAL) / total); // Subtract out ticks used at beginning
+ });
+ debug(`\nRegistry: ${util.inspect(registry.rules, { depth: null })}`);
+
+ // Create a list of recommended rules, because we don't want to disable them
+ const recRules = Object.keys(recConfig.rules).filter(ruleId => ConfigOps.isErrorSeverity(recConfig.rules[ruleId]));
+
+ // Find and disable rules which had no error-free configuration
+ const failingRegistry = registry.getFailingRulesRegistry();
+
+ Object.keys(failingRegistry.rules).forEach(ruleId => {
+
+ // If the rule is recommended, set it to error, otherwise disable it
+ disabledConfigs[ruleId] = (recRules.indexOf(ruleId) !== -1) ? 2 : 0;
+ });
+
+ // Now that we know which rules to disable, strip out configs with errors
+ registry = registry.stripFailingConfigs();
+
+ /*
+ * If there is only one config that results in no errors for a rule, we should use it.
+ * createConfig will only add rules that have one configuration in the registry.
+ */
+ const singleConfigs = registry.createConfig().rules;
+
+ /*
+ * The "sweet spot" for number of options in a config seems to be two (severity plus one option).
+ * Very often, a third option (usually an object) is available to address
+ * edge cases, exceptions, or unique situations. We will prefer to use a config with
+ * specificity of two.
+ */
+ const specTwoConfigs = registry.filterBySpecificity(2).createConfig().rules;
+
+ // Maybe a specific combination using all three options works
+ const specThreeConfigs = registry.filterBySpecificity(3).createConfig().rules;
+
+ // If all else fails, try to use the default (severity only)
+ const defaultConfigs = registry.filterBySpecificity(1).createConfig().rules;
+
+ // Combine configs in reverse priority order (later take precedence)
+ newConfig.rules = Object.assign({}, disabledConfigs, defaultConfigs, specThreeConfigs, specTwoConfigs, singleConfigs);
+
+ // Make sure progress bar has finished (floating point rounding)
+ bar.update(BAR_TOTAL);
+
+ // Log out some stats to let the user know what happened
+ const finalRuleIds = Object.keys(newConfig.rules);
+ const totalRules = finalRuleIds.length;
+ const enabledRules = finalRuleIds.filter(ruleId => (newConfig.rules[ruleId] !== 0)).length;
+ const resultMessage = [
+ `\nEnabled ${enabledRules} out of ${totalRules}`,
+ `rules based on ${fileQty}`,
+ `file${(fileQty === 1) ? "." : "s."}`
+ ].join(" ");
+
+ log.info(resultMessage);
+
+ ConfigOps.normalizeToStrings(newConfig);
+ return newConfig;
+}
+
+/**
+ * process user's answers and create config object
+ * @param {Object} answers answers received from inquirer
+ * @returns {Object} config object
+ */
+function processAnswers(answers) {
+ let config = { rules: {}, env: {} };
+
+ if (answers.es6) {
+ config.env.es6 = true;
+ if (answers.modules) {
+ config.parserOptions = config.parserOptions || {};
+ config.parserOptions.sourceType = "module";
+ }
+ }
+ if (answers.commonjs) {
+ config.env.commonjs = true;
+ }
+ answers.env.forEach(env => {
+ config.env[env] = true;
+ });
+ if (answers.jsx) {
+ config.parserOptions = config.parserOptions || {};
+ config.parserOptions.ecmaFeatures = config.parserOptions.ecmaFeatures || {};
+ config.parserOptions.ecmaFeatures.jsx = true;
+ if (answers.react) {
+ config.plugins = ["react"];
+ config.parserOptions.ecmaFeatures.experimentalObjectRestSpread = true;
+ }
+ }
+
+ if (answers.source === "prompt") {
+ config.extends = "eslint:recommended";
+ config.rules.indent = ["error", answers.indent];
+ config.rules.quotes = ["error", answers.quotes];
+ config.rules["linebreak-style"] = ["error", answers.linebreak];
+ config.rules.semi = ["error", answers.semi ? "always" : "never"];
+ }
+
+ installModules(config);
+
+ if (answers.source === "auto") {
+ config = configureRules(answers, config);
+ config = autoconfig.extendFromRecommended(config);
+ }
+
+ ConfigOps.normalizeToStrings(config);
+ return config;
+}
+
+/**
+ * process user's style guide of choice and return an appropriate config object.
+ * @param {string} guide name of the chosen style guide
+ * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
+ * @returns {Object} config object
+ */
+function getConfigForStyleGuide(guide, installESLint) {
+ const guides = {
+ google: { extends: "google" },
+ airbnb: { extends: "airbnb" },
+ "airbnb-base": { extends: "airbnb-base" },
+ standard: { extends: "standard" }
+ };
+
+ if (!guides[guide]) {
+ throw new Error("You referenced an unsupported guide.");
+ }
+
+ installModules(guides[guide], installESLint);
+
+ return guides[guide];
+}
+
+/**
+ * Get the version of the local ESLint.
+ * @returns {string|null} The version. If the local ESLint was not found, returns null.
+ */
+function getLocalESLintVersion() {
+ try {
+ const resolver = new ModuleResolver();
+ const eslintPath = resolver.resolve("eslint", process.cwd());
+ const eslint = require(eslintPath);
+
+ return eslint.linter.version || null;
+ } catch (_err) {
+ return null;
+ }
+}
+
+/**
+ * Get the shareable config name of the chosen style guide.
+ * @param {Object} answers The answers object.
+ * @returns {string} The shareable config name.
+ */
+function getStyleGuideName(answers) {
+ if (answers.styleguide === "airbnb" && !answers.airbnbReact) {
+ return "airbnb-base";
+ }
+ return answers.styleguide;
+}
+
+/**
+ * Check whether the local ESLint version conflicts with the required version of the chosen shareable config.
+ * @param {Object} answers The answers object.
+ * @returns {boolean} `true` if the local ESLint is found then it conflicts with the required version of the chosen shareable config.
+ */
+function hasESLintVersionConflict(answers) {
+
+ // Get the local ESLint version.
+ const localESLintVersion = getLocalESLintVersion();
+
+ if (!localESLintVersion) {
+ return false;
+ }
+
+ // Get the required range of ESLint version.
+ const configName = getStyleGuideName(answers);
+ const moduleName = `eslint-config-${configName}@latest`;
+ const peerDependencies = getPeerDependencies(moduleName) || {};
+ const requiredESLintVersionRange = peerDependencies.eslint;
+
+ if (!requiredESLintVersionRange) {
+ return false;
+ }
+
+ answers.localESLintVersion = localESLintVersion;
+ answers.requiredESLintVersionRange = requiredESLintVersionRange;
+
+ // Check the version.
+ if (semver.satisfies(localESLintVersion, requiredESLintVersionRange)) {
+ answers.installESLint = false;
+ return false;
+ }
+
+ return true;
+}
+
+/* istanbul ignore next: no need to test inquirer*/
+/**
+ * Ask use a few questions on command prompt
+ * @returns {Promise} The promise with the result of the prompt
+ */
+function promptUser() {
+
+ return inquirer.prompt([
+ {
+ type: "list",
+ name: "source",
+ message: "How would you like to configure ESLint?",
+ default: "prompt",
+ choices: [
+ { name: "Answer questions about your style", value: "prompt" },
+ { name: "Use a popular style guide", value: "guide" },
+ { name: "Inspect your JavaScript file(s)", value: "auto" }
+ ]
+ },
+ {
+ type: "list",
+ name: "styleguide",
+ message: "Which style guide do you want to follow?",
+ choices: [{ name: "Google", value: "google" }, { name: "Airbnb", value: "airbnb" }, { name: "Standard", value: "standard" }],
+ when(answers) {
+ answers.packageJsonExists = npmUtil.checkPackageJson();
+ return answers.source === "guide" && answers.packageJsonExists;
+ }
+ },
+ {
+ type: "confirm",
+ name: "airbnbReact",
+ message: "Do you use React?",
+ default: false,
+ when(answers) {
+ return answers.styleguide === "airbnb";
+ }
+ },
+ {
+ type: "input",
+ name: "patterns",
+ message: "Which file(s), path(s), or glob(s) should be examined?",
+ when(answers) {
+ return (answers.source === "auto");
+ },
+ validate(input) {
+ if (input.trim().length === 0 && input.trim() !== ",") {
+ return "You must tell us what code to examine. Try again.";
+ }
+ return true;
+ }
+ },
+ {
+ type: "list",
+ name: "format",
+ message: "What format do you want your config file to be in?",
+ default: "JavaScript",
+ choices: ["JavaScript", "YAML", "JSON"],
+ when(answers) {
+ return ((answers.source === "guide" && answers.packageJsonExists) || answers.source === "auto");
+ }
+ },
+ {
+ type: "confirm",
+ name: "installESLint",
+ message(answers) {
+ const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange)
+ ? "upgrade"
+ : "downgrade";
+
+ return `The style guide "${answers.styleguide}" requires eslint@${answers.requiredESLintVersionRange}. You are currently using eslint@${answers.localESLintVersion}.\n Do you want to ${verb}?`;
+ },
+ default: true,
+ when(answers) {
+ return answers.source === "guide" && answers.packageJsonExists && hasESLintVersionConflict(answers);
+ }
+ }
+ ]).then(earlyAnswers => {
+
+ // early exit if you are using a style guide
+ if (earlyAnswers.source === "guide") {
+ if (!earlyAnswers.packageJsonExists) {
+ log.info("A package.json is necessary to install plugins such as style guides. Run `npm init` to create a package.json file and try again.");
+ return void 0;
+ }
+ if (earlyAnswers.installESLint === false && !semver.satisfies(earlyAnswers.localESLintVersion, earlyAnswers.requiredESLintVersionRange)) {
+ log.info(`Note: it might not work since ESLint's version is mismatched with the ${earlyAnswers.styleguide} config.`);
+ }
+ if (earlyAnswers.styleguide === "airbnb" && !earlyAnswers.airbnbReact) {
+ earlyAnswers.styleguide = "airbnb-base";
+ }
+
+ const config = getConfigForStyleGuide(earlyAnswers.styleguide, earlyAnswers.installESLint);
+
+ writeFile(config, earlyAnswers.format);
+
+ return void 0;
+ }
+
+ // continue with the questions otherwise...
+ return inquirer.prompt([
+ {
+ type: "confirm",
+ name: "es6",
+ message: "Are you using ECMAScript 6 features?",
+ default: false
+ },
+ {
+ type: "confirm",
+ name: "modules",
+ message: "Are you using ES6 modules?",
+ default: false,
+ when(answers) {
+ return answers.es6 === true;
+ }
+ },
+ {
+ type: "checkbox",
+ name: "env",
+ message: "Where will your code run?",
+ default: ["browser"],
+ choices: [{ name: "Browser", value: "browser" }, { name: "Node", value: "node" }]
+ },
+ {
+ type: "confirm",
+ name: "commonjs",
+ message: "Do you use CommonJS?",
+ default: false,
+ when(answers) {
+ return answers.env.some(env => env === "browser");
+ }
+ },
+ {
+ type: "confirm",
+ name: "jsx",
+ message: "Do you use JSX?",
+ default: false
+ },
+ {
+ type: "confirm",
+ name: "react",
+ message: "Do you use React?",
+ default: false,
+ when(answers) {
+ return answers.jsx;
+ }
+ }
+ ]).then(secondAnswers => {
+
+ // early exit if you are using automatic style generation
+ if (earlyAnswers.source === "auto") {
+ const combinedAnswers = Object.assign({}, earlyAnswers, secondAnswers);
+
+ const config = processAnswers(combinedAnswers);
+
+ installModules(config);
+ writeFile(config, earlyAnswers.format);
+
+ return void 0;
+ }
+
+ // continue with the style questions otherwise...
+ return inquirer.prompt([
+ {
+ type: "list",
+ name: "indent",
+ message: "What style of indentation do you use?",
+ default: "tab",
+ choices: [{ name: "Tabs", value: "tab" }, { name: "Spaces", value: 4 }]
+ },
+ {
+ type: "list",
+ name: "quotes",
+ message: "What quotes do you use for strings?",
+ default: "double",
+ choices: [{ name: "Double", value: "double" }, { name: "Single", value: "single" }]
+ },
+ {
+ type: "list",
+ name: "linebreak",
+ message: "What line endings do you use?",
+ default: "unix",
+ choices: [{ name: "Unix", value: "unix" }, { name: "Windows", value: "windows" }]
+ },
+ {
+ type: "confirm",
+ name: "semi",
+ message: "Do you require semicolons?",
+ default: true
+ },
+ {
+ type: "list",
+ name: "format",
+ message: "What format do you want your config file to be in?",
+ default: "JavaScript",
+ choices: ["JavaScript", "YAML", "JSON"]
+ }
+ ]).then(answers => {
+ const totalAnswers = Object.assign({}, earlyAnswers, secondAnswers, answers);
+
+ const config = processAnswers(totalAnswers);
+
+ installModules(config);
+ writeFile(config, answers.format);
+ });
+ });
+ });
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+const init = {
+ getConfigForStyleGuide,
+ hasESLintVersionConflict,
+ processAnswers,
+ /* istanbul ignore next */initializeConfig() {
+ return promptUser();
+ }
+};
+
+module.exports = init;
diff --git a/tools/node_modules/eslint/lib/config/config-ops.js b/tools/node_modules/eslint/lib/config/config-ops.js
new file mode 100644
index 0000000000..2ce500a4f4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-ops.js
@@ -0,0 +1,383 @@
+/**
+ * @fileoverview Config file operations. This file must be usable in the browser,
+ * so no Node-specific code can be here.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const minimatch = require("minimatch"),
+ path = require("path");
+
+const debug = require("debug")("eslint:config-ops");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const RULE_SEVERITY_STRINGS = ["off", "warn", "error"],
+ RULE_SEVERITY = RULE_SEVERITY_STRINGS.reduce((map, value, index) => {
+ map[value] = index;
+ return map;
+ }, {}),
+ VALID_SEVERITIES = [0, 1, 2, "off", "warn", "error"];
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ /**
+ * Creates an empty configuration object suitable for merging as a base.
+ * @returns {Object} A configuration object.
+ */
+ createEmptyConfig() {
+ return {
+ globals: {},
+ env: {},
+ rules: {},
+ parserOptions: {}
+ };
+ },
+
+ /**
+ * Creates an environment config based on the specified environments.
+ * @param {Object<string,boolean>} env The environment settings.
+ * @param {Environments} envContext The environment context.
+ * @returns {Object} A configuration object with the appropriate rules and globals
+ * set.
+ */
+ createEnvironmentConfig(env, envContext) {
+
+ const envConfig = this.createEmptyConfig();
+
+ if (env) {
+
+ envConfig.env = env;
+
+ Object.keys(env).filter(name => env[name]).forEach(name => {
+ const environment = envContext.get(name);
+
+ if (environment) {
+ debug(`Creating config for environment ${name}`);
+ if (environment.globals) {
+ Object.assign(envConfig.globals, environment.globals);
+ }
+
+ if (environment.parserOptions) {
+ Object.assign(envConfig.parserOptions, environment.parserOptions);
+ }
+ }
+ });
+ }
+
+ return envConfig;
+ },
+
+ /**
+ * Given a config with environment settings, applies the globals and
+ * ecmaFeatures to the configuration and returns the result.
+ * @param {Object} config The configuration information.
+ * @param {Environments} envContent env context.
+ * @returns {Object} The updated configuration information.
+ */
+ applyEnvironments(config, envContent) {
+ if (config.env && typeof config.env === "object") {
+ debug("Apply environment settings to config");
+ return this.merge(this.createEnvironmentConfig(config.env, envContent), config);
+ }
+
+ return config;
+ },
+
+ /**
+ * Merges two config objects. This will not only add missing keys, but will also modify values to match.
+ * @param {Object} target config object
+ * @param {Object} src config object. Overrides in this config object will take priority over base.
+ * @param {boolean} [combine] Whether to combine arrays or not
+ * @param {boolean} [isRule] Whether its a rule
+ * @returns {Object} merged config object.
+ */
+ merge: function deepmerge(target, src, combine, isRule) {
+
+ /*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2012 Nicholas Fisher
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+ /*
+ * This code is taken from deepmerge repo
+ * (https://github.com/KyleAMathews/deepmerge)
+ * and modified to meet our needs.
+ */
+ const array = Array.isArray(src) || Array.isArray(target);
+ let dst = array && [] || {};
+
+ combine = !!combine;
+ isRule = !!isRule;
+ if (array) {
+ target = target || [];
+
+ // src could be a string, so check for array
+ if (isRule && Array.isArray(src) && src.length > 1) {
+ dst = dst.concat(src);
+ } else {
+ dst = dst.concat(target);
+ }
+ if (typeof src !== "object" && !Array.isArray(src)) {
+ src = [src];
+ }
+ Object.keys(src).forEach((e, i) => {
+ e = src[i];
+ if (typeof dst[i] === "undefined") {
+ dst[i] = e;
+ } else if (typeof e === "object") {
+ if (isRule) {
+ dst[i] = e;
+ } else {
+ dst[i] = deepmerge(target[i], e, combine, isRule);
+ }
+ } else {
+ if (!combine) {
+ dst[i] = e;
+ } else {
+ if (dst.indexOf(e) === -1) {
+ dst.push(e);
+ }
+ }
+ }
+ });
+ } else {
+ if (target && typeof target === "object") {
+ Object.keys(target).forEach(key => {
+ dst[key] = target[key];
+ });
+ }
+ Object.keys(src).forEach(key => {
+ if (key === "overrides") {
+ dst[key] = (target[key] || []).concat(src[key] || []);
+ } else if (Array.isArray(src[key]) || Array.isArray(target[key])) {
+ dst[key] = deepmerge(target[key], src[key], key === "plugins" || key === "extends", isRule);
+ } else if (typeof src[key] !== "object" || !src[key] || key === "exported" || key === "astGlobals") {
+ dst[key] = src[key];
+ } else {
+ dst[key] = deepmerge(target[key] || {}, src[key], combine, key === "rules");
+ }
+ });
+ }
+
+ return dst;
+ },
+
+ /**
+ * Normalizes the severity value of a rule's configuration to a number
+ * @param {(number|string|[number, ...*]|[string, ...*])} ruleConfig A rule's configuration value, generally
+ * received from the user. A valid config value is either 0, 1, 2, the string "off" (treated the same as 0),
+ * the string "warn" (treated the same as 1), the string "error" (treated the same as 2), or an array
+ * whose first element is one of the above values. Strings are matched case-insensitively.
+ * @returns {(0|1|2)} The numeric severity value if the config value was valid, otherwise 0.
+ */
+ getRuleSeverity(ruleConfig) {
+ const severityValue = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
+
+ if (severityValue === 0 || severityValue === 1 || severityValue === 2) {
+ return severityValue;
+ }
+
+ if (typeof severityValue === "string") {
+ return RULE_SEVERITY[severityValue.toLowerCase()] || 0;
+ }
+
+ return 0;
+ },
+
+ /**
+ * Converts old-style severity settings (0, 1, 2) into new-style
+ * severity settings (off, warn, error) for all rules. Assumption is that severity
+ * values have already been validated as correct.
+ * @param {Object} config The config object to normalize.
+ * @returns {void}
+ */
+ normalizeToStrings(config) {
+
+ if (config.rules) {
+ Object.keys(config.rules).forEach(ruleId => {
+ const ruleConfig = config.rules[ruleId];
+
+ if (typeof ruleConfig === "number") {
+ config.rules[ruleId] = RULE_SEVERITY_STRINGS[ruleConfig] || RULE_SEVERITY_STRINGS[0];
+ } else if (Array.isArray(ruleConfig) && typeof ruleConfig[0] === "number") {
+ ruleConfig[0] = RULE_SEVERITY_STRINGS[ruleConfig[0]] || RULE_SEVERITY_STRINGS[0];
+ }
+ });
+ }
+ },
+
+ /**
+ * Determines if the severity for the given rule configuration represents an error.
+ * @param {int|string|Array} ruleConfig The configuration for an individual rule.
+ * @returns {boolean} True if the rule represents an error, false if not.
+ */
+ isErrorSeverity(ruleConfig) {
+
+ let severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
+
+ if (typeof severity === "string") {
+ severity = RULE_SEVERITY[severity.toLowerCase()] || 0;
+ }
+
+ return (typeof severity === "number" && severity === 2);
+ },
+
+ /**
+ * Checks whether a given config has valid severity or not.
+ * @param {number|string|Array} ruleConfig - The configuration for an individual rule.
+ * @returns {boolean} `true` if the configuration has valid severity.
+ */
+ isValidSeverity(ruleConfig) {
+ let severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
+
+ if (typeof severity === "string") {
+ severity = severity.toLowerCase();
+ }
+ return VALID_SEVERITIES.indexOf(severity) !== -1;
+ },
+
+ /**
+ * Checks whether every rule of a given config has valid severity or not.
+ * @param {Object} config - The configuration for rules.
+ * @returns {boolean} `true` if the configuration has valid severity.
+ */
+ isEverySeverityValid(config) {
+ return Object.keys(config).every(ruleId => this.isValidSeverity(config[ruleId]));
+ },
+
+ /**
+ * Merges all configurations in a given config vector. A vector is an array of objects, each containing a config
+ * file path and a list of subconfig indices that match the current file path. All config data is assumed to be
+ * cached.
+ * @param {Array<Object>} vector list of config files and their subconfig indices that match the current file path
+ * @param {Object} configCache the config cache
+ * @returns {Object} config object
+ */
+ getConfigFromVector(vector, configCache) {
+
+ const cachedConfig = configCache.getMergedVectorConfig(vector);
+
+ if (cachedConfig) {
+ return cachedConfig;
+ }
+
+ debug("Using config from partial cache");
+
+ const subvector = Array.from(vector);
+ let nearestCacheIndex = subvector.length - 1,
+ partialCachedConfig;
+
+ while (nearestCacheIndex >= 0) {
+ partialCachedConfig = configCache.getMergedVectorConfig(subvector);
+ if (partialCachedConfig) {
+ break;
+ }
+ subvector.pop();
+ nearestCacheIndex--;
+ }
+
+ if (!partialCachedConfig) {
+ partialCachedConfig = {};
+ }
+
+ let finalConfig = partialCachedConfig;
+
+ // Start from entry immediately following nearest cached config (first uncached entry)
+ for (let i = nearestCacheIndex + 1; i < vector.length; i++) {
+ finalConfig = this.mergeVectorEntry(finalConfig, vector[i], configCache);
+ configCache.setMergedVectorConfig(vector.slice(0, i + 1), finalConfig);
+ }
+
+ return finalConfig;
+ },
+
+ /**
+ * Merges the config options from a single vector entry into the supplied config.
+ * @param {Object} config the base config to merge the vector entry's options into
+ * @param {Object} vectorEntry a single entry from a vector, consisting of a config file path and an array of
+ * matching override indices
+ * @param {Object} configCache the config cache
+ * @returns {Object} merged config object
+ */
+ mergeVectorEntry(config, vectorEntry, configCache) {
+ const vectorEntryConfig = Object.assign({}, configCache.getConfig(vectorEntry.filePath));
+ let mergedConfig = Object.assign({}, config),
+ overrides;
+
+ if (vectorEntryConfig.overrides) {
+ overrides = vectorEntryConfig.overrides.filter(
+ (override, overrideIndex) => vectorEntry.matchingOverrides.indexOf(overrideIndex) !== -1
+ );
+ } else {
+ overrides = [];
+ }
+
+ mergedConfig = this.merge(mergedConfig, vectorEntryConfig);
+
+ delete mergedConfig.overrides;
+
+ mergedConfig = overrides.reduce((lastConfig, override) => this.merge(lastConfig, override), mergedConfig);
+
+ if (mergedConfig.filePath) {
+ delete mergedConfig.filePath;
+ delete mergedConfig.baseDirectory;
+ } else if (mergedConfig.files) {
+ delete mergedConfig.files;
+ }
+
+ return mergedConfig;
+ },
+
+ /**
+ * Checks that the specified file path matches all of the supplied glob patterns.
+ * @param {string} filePath The file path to test patterns against
+ * @param {string|string[]} patterns One or more glob patterns, of which at least one should match the file path
+ * @param {string|string[]} [excludedPatterns] One or more glob patterns, of which none should match the file path
+ * @returns {boolean} True if all the supplied patterns match the file path, false otherwise
+ */
+ pathMatchesGlobs(filePath, patterns, excludedPatterns) {
+ const patternList = [].concat(patterns);
+ const excludedPatternList = [].concat(excludedPatterns || []);
+
+ patternList.concat(excludedPatternList).forEach(pattern => {
+ if (path.isAbsolute(pattern) || pattern.includes("..")) {
+ throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
+ }
+ });
+
+ const opts = { matchBase: true };
+
+ return patternList.some(pattern => minimatch(filePath, pattern, opts)) &&
+ !excludedPatternList.some(excludedPattern => minimatch(filePath, excludedPattern, opts));
+ }
+};
diff --git a/tools/node_modules/eslint/lib/config/config-rule.js b/tools/node_modules/eslint/lib/config/config-rule.js
new file mode 100644
index 0000000000..5fc38ac5d1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-rule.js
@@ -0,0 +1,322 @@
+/**
+ * @fileoverview Create configurations for a rule
+ * @author Ian VanSchooten
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Rules = require("../rules"),
+ loadRules = require("../load-rules");
+
+const rules = new Rules();
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Wrap all of the elements of an array into arrays.
+ * @param {*[]} xs Any array.
+ * @returns {Array[]} An array of arrays.
+ */
+function explodeArray(xs) {
+ return xs.reduce((accumulator, x) => {
+ accumulator.push([x]);
+ return accumulator;
+ }, []);
+}
+
+/**
+ * Mix two arrays such that each element of the second array is concatenated
+ * onto each element of the first array.
+ *
+ * For example:
+ * combineArrays([a, [b, c]], [x, y]); // -> [[a, x], [a, y], [b, c, x], [b, c, y]]
+ *
+ * @param {array} arr1 The first array to combine.
+ * @param {array} arr2 The second array to combine.
+ * @returns {array} A mixture of the elements of the first and second arrays.
+ */
+function combineArrays(arr1, arr2) {
+ const res = [];
+
+ if (arr1.length === 0) {
+ return explodeArray(arr2);
+ }
+ if (arr2.length === 0) {
+ return explodeArray(arr1);
+ }
+ arr1.forEach(x1 => {
+ arr2.forEach(x2 => {
+ res.push([].concat(x1, x2));
+ });
+ });
+ return res;
+}
+
+/**
+ * Group together valid rule configurations based on object properties
+ *
+ * e.g.:
+ * groupByProperty([
+ * {before: true},
+ * {before: false},
+ * {after: true},
+ * {after: false}
+ * ]);
+ *
+ * will return:
+ * [
+ * [{before: true}, {before: false}],
+ * [{after: true}, {after: false}]
+ * ]
+ *
+ * @param {Object[]} objects Array of objects, each with one property/value pair
+ * @returns {Array[]} Array of arrays of objects grouped by property
+ */
+function groupByProperty(objects) {
+ const groupedObj = objects.reduce((accumulator, obj) => {
+ const prop = Object.keys(obj)[0];
+
+ accumulator[prop] = accumulator[prop] ? accumulator[prop].concat(obj) : [obj];
+ return accumulator;
+ }, {});
+
+ return Object.keys(groupedObj).map(prop => groupedObj[prop]);
+}
+
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Configuration settings for a rule.
+ *
+ * A configuration can be a single number (severity), or an array where the first
+ * element in the array is the severity, and is the only required element.
+ * Configs may also have one or more additional elements to specify rule
+ * configuration or options.
+ *
+ * @typedef {array|number} ruleConfig
+ * @param {number} 0 The rule's severity (0, 1, 2).
+ */
+
+/**
+ * Object whose keys are rule names and values are arrays of valid ruleConfig items
+ * which should be linted against the target source code to determine error counts.
+ * (a ruleConfigSet.ruleConfigs).
+ *
+ * e.g. rulesConfig = {
+ * "comma-dangle": [2, [2, "always"], [2, "always-multiline"], [2, "never"]],
+ * "no-console": [2]
+ * }
+ * @typedef rulesConfig
+ */
+
+
+/**
+ * Create valid rule configurations by combining two arrays,
+ * with each array containing multiple objects each with a
+ * single property/value pair and matching properties.
+ *
+ * e.g.:
+ * combinePropertyObjects(
+ * [{before: true}, {before: false}],
+ * [{after: true}, {after: false}]
+ * );
+ *
+ * will return:
+ * [
+ * {before: true, after: true},
+ * {before: true, after: false},
+ * {before: false, after: true},
+ * {before: false, after: false}
+ * ]
+ *
+ * @param {Object[]} objArr1 Single key/value objects, all with the same key
+ * @param {Object[]} objArr2 Single key/value objects, all with another key
+ * @returns {Object[]} Combined objects for each combination of input properties and values
+ */
+function combinePropertyObjects(objArr1, objArr2) {
+ const res = [];
+
+ if (objArr1.length === 0) {
+ return objArr2;
+ }
+ if (objArr2.length === 0) {
+ return objArr1;
+ }
+ objArr1.forEach(obj1 => {
+ objArr2.forEach(obj2 => {
+ const combinedObj = {};
+ const obj1Props = Object.keys(obj1);
+ const obj2Props = Object.keys(obj2);
+
+ obj1Props.forEach(prop1 => {
+ combinedObj[prop1] = obj1[prop1];
+ });
+ obj2Props.forEach(prop2 => {
+ combinedObj[prop2] = obj2[prop2];
+ });
+ res.push(combinedObj);
+ });
+ });
+ return res;
+}
+
+/**
+ * Creates a new instance of a rule configuration set
+ *
+ * A rule configuration set is an array of configurations that are valid for a
+ * given rule. For example, the configuration set for the "semi" rule could be:
+ *
+ * ruleConfigSet.ruleConfigs // -> [[2], [2, "always"], [2, "never"]]
+ *
+ * Rule configuration set class
+ */
+class RuleConfigSet {
+
+ /**
+ * @param {ruleConfig[]} configs Valid rule configurations
+ */
+ constructor(configs) {
+
+ /**
+ * Stored valid rule configurations for this instance
+ * @type {array}
+ */
+ this.ruleConfigs = configs || [];
+ }
+
+ /**
+ * Add a severity level to the front of all configs in the instance.
+ * This should only be called after all configs have been added to the instance.
+ *
+ * @param {number} [severity=2] The level of severity for the rule (0, 1, 2)
+ * @returns {void}
+ */
+ addErrorSeverity(severity) {
+ severity = severity || 2;
+
+ this.ruleConfigs = this.ruleConfigs.map(config => {
+ config.unshift(severity);
+ return config;
+ });
+
+ // Add a single config at the beginning consisting of only the severity
+ this.ruleConfigs.unshift(severity);
+ }
+
+ /**
+ * Add rule configs from an array of strings (schema enums)
+ * @param {string[]} enums Array of valid rule options (e.g. ["always", "never"])
+ * @returns {void}
+ */
+ addEnums(enums) {
+ this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, enums));
+ }
+
+ /**
+ * Add rule configurations from a schema object
+ * @param {Object} obj Schema item with type === "object"
+ * @returns {boolean} true if at least one schema for the object could be generated, false otherwise
+ */
+ addObject(obj) {
+ const objectConfigSet = {
+ objectConfigs: [],
+ add(property, values) {
+ for (let idx = 0; idx < values.length; idx++) {
+ const optionObj = {};
+
+ optionObj[property] = values[idx];
+ this.objectConfigs.push(optionObj);
+ }
+ },
+
+ combine() {
+ this.objectConfigs = groupByProperty(this.objectConfigs).reduce((accumulator, objArr) => combinePropertyObjects(accumulator, objArr), []);
+ }
+ };
+
+ /*
+ * The object schema could have multiple independent properties.
+ * If any contain enums or booleans, they can be added and then combined
+ */
+ Object.keys(obj.properties).forEach(prop => {
+ if (obj.properties[prop].enum) {
+ objectConfigSet.add(prop, obj.properties[prop].enum);
+ }
+ if (obj.properties[prop].type && obj.properties[prop].type === "boolean") {
+ objectConfigSet.add(prop, [true, false]);
+ }
+ });
+ objectConfigSet.combine();
+
+ if (objectConfigSet.objectConfigs.length > 0) {
+ this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, objectConfigSet.objectConfigs));
+ return true;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Generate valid rule configurations based on a schema object
+ * @param {Object} schema A rule's schema object
+ * @returns {array[]} Valid rule configurations
+ */
+function generateConfigsFromSchema(schema) {
+ const configSet = new RuleConfigSet();
+
+ if (Array.isArray(schema)) {
+ for (const opt of schema) {
+ if (opt.enum) {
+ configSet.addEnums(opt.enum);
+ } else if (opt.type && opt.type === "object") {
+ if (!configSet.addObject(opt)) {
+ break;
+ }
+
+ // TODO (IanVS): support oneOf
+ } else {
+
+ // If we don't know how to fill in this option, don't fill in any of the following options.
+ break;
+ }
+ }
+ }
+ configSet.addErrorSeverity();
+ return configSet.ruleConfigs;
+}
+
+/**
+ * Generate possible rule configurations for all of the core rules
+ * @returns {rulesConfig} Hash of rule names and arrays of possible configurations
+ */
+function createCoreRuleConfigs() {
+ const ruleList = loadRules();
+
+ return Object.keys(ruleList).reduce((accumulator, id) => {
+ const rule = rules.get(id);
+ const schema = (typeof rule === "function") ? rule.schema : rule.meta.schema;
+
+ accumulator[id] = generateConfigsFromSchema(schema);
+ return accumulator;
+ }, {});
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ generateConfigsFromSchema,
+ createCoreRuleConfigs
+};
diff --git a/tools/node_modules/eslint/lib/config/config-validator.js b/tools/node_modules/eslint/lib/config/config-validator.js
new file mode 100644
index 0000000000..3d98b51045
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-validator.js
@@ -0,0 +1,244 @@
+/**
+ * @fileoverview Validates configs.
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const ajv = require("../util/ajv"),
+ lodash = require("lodash"),
+ configSchema = require("../../conf/config-schema.js"),
+ util = require("util");
+
+const ruleValidators = new WeakMap();
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+let validateSchema;
+
+/**
+ * Gets a complete options schema for a rule.
+ * @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
+ * @returns {Object} JSON Schema for the rule's options.
+ */
+function getRuleOptionsSchema(rule) {
+ const schema = rule.schema || rule.meta && rule.meta.schema;
+
+ // Given a tuple of schemas, insert warning level at the beginning
+ if (Array.isArray(schema)) {
+ if (schema.length) {
+ return {
+ type: "array",
+ items: schema,
+ minItems: 0,
+ maxItems: schema.length
+ };
+ }
+ return {
+ type: "array",
+ minItems: 0,
+ maxItems: 0
+ };
+
+ }
+
+ // Given a full schema, leave it alone
+ return schema || null;
+}
+
+/**
+ * Validates a rule's severity and returns the severity value. Throws an error if the severity is invalid.
+ * @param {options} options The given options for the rule.
+ * @returns {number|string} The rule's severity value
+ */
+function validateRuleSeverity(options) {
+ const severity = Array.isArray(options) ? options[0] : options;
+
+ if (severity !== 0 && severity !== 1 && severity !== 2 && !(typeof severity === "string" && /^(?:off|warn|error)$/i.test(severity))) {
+ throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util.inspect(severity).replace(/'/g, "\"").replace(/\n/g, "")}').\n`);
+ }
+
+ return severity;
+}
+
+/**
+ * Validates the non-severity options passed to a rule, based on its schema.
+ * @param {{create: Function}} rule The rule to validate
+ * @param {array} localOptions The options for the rule, excluding severity
+ * @returns {void}
+ */
+function validateRuleSchema(rule, localOptions) {
+ if (!ruleValidators.has(rule)) {
+ const schema = getRuleOptionsSchema(rule);
+
+ if (schema) {
+ ruleValidators.set(rule, ajv.compile(schema));
+ }
+ }
+
+ const validateRule = ruleValidators.get(rule);
+
+ if (validateRule) {
+ validateRule(localOptions);
+ if (validateRule.errors) {
+ throw new Error(validateRule.errors.map(
+ error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
+ ).join(""));
+ }
+ }
+}
+
+/**
+ * Validates a rule's options against its schema.
+ * @param {{create: Function}|null} rule The rule that the config is being validated for
+ * @param {string} ruleId The rule's unique name.
+ * @param {array|number} options The given options for the rule.
+ * @param {string} source The name of the configuration source to report in any errors.
+ * @returns {void}
+ */
+function validateRuleOptions(rule, ruleId, options, source) {
+ if (!rule) {
+ return;
+ }
+ try {
+ const severity = validateRuleSeverity(options);
+
+ if (severity !== 0 && !(typeof severity === "string" && severity.toLowerCase() === "off")) {
+ validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
+ }
+ } catch (err) {
+ throw new Error(`${source}:\n\tConfiguration for rule "${ruleId}" is invalid:\n${err.message}`);
+ }
+}
+
+/**
+ * Validates an environment object
+ * @param {Object} environment The environment config object to validate.
+ * @param {string} source The name of the configuration source to report in any errors.
+ * @param {Environments} envContext Env context
+ * @returns {void}
+ */
+function validateEnvironment(environment, source, envContext) {
+
+ // not having an environment is ok
+ if (!environment) {
+ return;
+ }
+
+ Object.keys(environment).forEach(env => {
+ if (!envContext.get(env)) {
+ const message = `${source}:\n\tEnvironment key "${env}" is unknown\n`;
+
+ throw new Error(message);
+ }
+ });
+}
+
+/**
+ * Validates a rules config object
+ * @param {Object} rulesConfig The rules config object to validate.
+ * @param {string} source The name of the configuration source to report in any errors.
+ * @param {function(string): {create: Function}} ruleMapper A mapper function from strings to loaded rules
+ * @returns {void}
+ */
+function validateRules(rulesConfig, source, ruleMapper) {
+ if (!rulesConfig) {
+ return;
+ }
+
+ Object.keys(rulesConfig).forEach(id => {
+ validateRuleOptions(ruleMapper(id), id, rulesConfig[id], source);
+ });
+}
+
+/**
+ * Formats an array of schema validation errors.
+ * @param {Array} errors An array of error messages to format.
+ * @returns {string} Formatted error message
+ */
+function formatErrors(errors) {
+ return errors.map(error => {
+ if (error.keyword === "additionalProperties") {
+ const formattedPropertyPath = error.dataPath.length ? `${error.dataPath.slice(1)}.${error.params.additionalProperty}` : error.params.additionalProperty;
+
+ return `Unexpected top-level property "${formattedPropertyPath}"`;
+ }
+ if (error.keyword === "type") {
+ const formattedField = error.dataPath.slice(1);
+ const formattedExpectedType = Array.isArray(error.schema) ? error.schema.join("/") : error.schema;
+ const formattedValue = JSON.stringify(error.data);
+
+ return `Property "${formattedField}" is the wrong type (expected ${formattedExpectedType} but got \`${formattedValue}\`)`;
+ }
+
+ const field = error.dataPath[0] === "." ? error.dataPath.slice(1) : error.dataPath;
+
+ return `"${field}" ${error.message}. Value: ${JSON.stringify(error.data)}`;
+ }).map(message => `\t- ${message}.\n`).join("");
+}
+
+/**
+ * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
+ * for each unique file path, but repeated invocations with the same file path have no effect.
+ * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active.
+ * @param {string} source The name of the configuration source to report the warning for.
+ * @returns {void}
+ */
+const emitEcmaFeaturesWarning = lodash.memoize(source => {
+
+ /*
+ * util.deprecate seems to be the only way to emit a warning in Node 4.x while respecting the --no-warnings flag.
+ * (In Node 6+, process.emitWarning could be used instead.)
+ */
+ util.deprecate(
+ () => {},
+ `[eslint] The 'ecmaFeatures' config file property is deprecated, and has no effect. (found in ${source})`
+ )();
+});
+
+/**
+ * Validates the top level properties of the config object.
+ * @param {Object} config The config object to validate.
+ * @param {string} source The name of the configuration source to report in any errors.
+ * @returns {void}
+ */
+function validateConfigSchema(config, source) {
+ validateSchema = validateSchema || ajv.compile(configSchema);
+
+ if (!validateSchema(config)) {
+ throw new Error(`ESLint configuration in ${source} is invalid:\n${formatErrors(validateSchema.errors)}`);
+ }
+
+ if (Object.prototype.hasOwnProperty.call(config, "ecmaFeatures")) {
+ emitEcmaFeaturesWarning(source);
+ }
+}
+
+/**
+ * Validates an entire config object.
+ * @param {Object} config The config object to validate.
+ * @param {string} source The name of the configuration source to report in any errors.
+ * @param {function(string): {create: Function}} ruleMapper A mapper function from rule IDs to defined rules
+ * @param {Environments} envContext The env context
+ * @returns {void}
+ */
+function validate(config, source, ruleMapper, envContext) {
+ validateConfigSchema(config, source);
+ validateRules(config.rules, source, ruleMapper);
+ validateEnvironment(config.env, source, envContext);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ getRuleOptionsSchema,
+ validate,
+ validateRuleOptions
+};
diff --git a/tools/node_modules/eslint/lib/config/environments.js b/tools/node_modules/eslint/lib/config/environments.js
new file mode 100644
index 0000000000..1ec9438af5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/environments.js
@@ -0,0 +1,84 @@
+/**
+ * @fileoverview Environments manager
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const envs = require("../../conf/environments");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+class Environments {
+
+ /**
+ * create env context
+ */
+ constructor() {
+ this._environments = new Map();
+
+ this.load();
+ }
+
+ /**
+ * Loads the default environments.
+ * @returns {void}
+ * @private
+ */
+ load() {
+ Object.keys(envs).forEach(envName => {
+ this._environments.set(envName, envs[envName]);
+ });
+ }
+
+ /**
+ * Gets the environment with the given name.
+ * @param {string} name The name of the environment to retrieve.
+ * @returns {Object?} The environment object or null if not found.
+ */
+ get(name) {
+ return this._environments.get(name) || null;
+ }
+
+ /**
+ * Gets all the environment present
+ * @returns {Object} The environment object for each env name
+ */
+ getAll() {
+ return Array.from(this._environments).reduce((coll, env) => {
+ coll[env[0]] = env[1];
+ return coll;
+ }, {});
+ }
+
+ /**
+ * Defines an environment.
+ * @param {string} name The name of the environment.
+ * @param {Object} env The environment settings.
+ * @returns {void}
+ */
+ define(name, env) {
+ this._environments.set(name, env);
+ }
+
+ /**
+ * Imports all environments from a plugin.
+ * @param {Object} plugin The plugin object.
+ * @param {string} pluginName The name of the plugin.
+ * @returns {void}
+ */
+ importPlugin(plugin, pluginName) {
+ if (plugin.environments) {
+ Object.keys(plugin.environments).forEach(envName => {
+ this.define(`${pluginName}/${envName}`, plugin.environments[envName]);
+ });
+ }
+ }
+}
+
+module.exports = Environments;
diff --git a/tools/node_modules/eslint/lib/config/plugins.js b/tools/node_modules/eslint/lib/config/plugins.js
new file mode 100644
index 0000000000..c509c48fe2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/plugins.js
@@ -0,0 +1,150 @@
+/**
+ * @fileoverview Plugins manager
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const debug = require("debug")("eslint:plugins");
+const naming = require("../util/naming");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const PLUGIN_NAME_PREFIX = "eslint-plugin-";
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Plugin class
+ */
+class Plugins {
+
+ /**
+ * Creates the plugins context
+ * @param {Environments} envContext - env context
+ * @param {Rules} rulesContext - rules context
+ */
+ constructor(envContext, rulesContext) {
+ this._plugins = Object.create(null);
+ this._environments = envContext;
+ this._rules = rulesContext;
+ }
+
+ /**
+ * Defines a plugin with a given name rather than loading from disk.
+ * @param {string} pluginName The name of the plugin to load.
+ * @param {Object} plugin The plugin object.
+ * @returns {void}
+ */
+ define(pluginName, plugin) {
+ const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
+ pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
+ pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
+ shortName = pluginNamespace + pluginNameWithoutPrefix;
+
+ // load up environments and rules
+ this._plugins[shortName] = plugin;
+ this._environments.importPlugin(plugin, shortName);
+ this._rules.importPlugin(plugin, shortName);
+ }
+
+ /**
+ * Gets a plugin with the given name.
+ * @param {string} pluginName The name of the plugin to retrieve.
+ * @returns {Object} The plugin or null if not loaded.
+ */
+ get(pluginName) {
+ return this._plugins[pluginName] || null;
+ }
+
+ /**
+ * Returns all plugins that are loaded.
+ * @returns {Object} The plugins cache.
+ */
+ getAll() {
+ return this._plugins;
+ }
+
+ /**
+ * Loads a plugin with the given name.
+ * @param {string} pluginName The name of the plugin to load.
+ * @returns {void}
+ * @throws {Error} If the plugin cannot be loaded.
+ */
+ load(pluginName) {
+ const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
+ pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
+ pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
+ shortName = pluginNamespace + pluginNameWithoutPrefix,
+ longName = pluginNamespace + PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix;
+ let plugin = null;
+
+ if (pluginName.match(/\s+/)) {
+ const whitespaceError = new Error(`Whitespace found in plugin name '${pluginName}'`);
+
+ whitespaceError.messageTemplate = "whitespace-found";
+ whitespaceError.messageData = {
+ pluginName: longName
+ };
+ throw whitespaceError;
+ }
+
+ if (!this._plugins[shortName]) {
+ try {
+ plugin = require(longName);
+ } catch (pluginLoadErr) {
+ try {
+
+ // Check whether the plugin exists
+ require.resolve(longName);
+ } catch (missingPluginErr) {
+
+ // If the plugin can't be resolved, display the missing plugin error (usually a config or install error)
+ debug(`Failed to load plugin ${longName}.`);
+ missingPluginErr.message = `Failed to load plugin ${pluginName}: ${missingPluginErr.message}`;
+ missingPluginErr.messageTemplate = "plugin-missing";
+ missingPluginErr.messageData = {
+ pluginName: longName
+ };
+ throw missingPluginErr;
+ }
+
+ // Otherwise, the plugin exists and is throwing on module load for some reason, so print the stack trace.
+ throw pluginLoadErr;
+ }
+
+ this.define(pluginName, plugin);
+ }
+ }
+
+ /**
+ * Loads all plugins from an array.
+ * @param {string[]} pluginNames An array of plugins names.
+ * @returns {void}
+ * @throws {Error} If a plugin cannot be loaded.
+ * @throws {Error} If "plugins" in config is not an array
+ */
+ loadAll(pluginNames) {
+
+ // if "plugins" in config is not an array, throw an error so user can fix their config.
+ if (!Array.isArray(pluginNames)) {
+ const pluginNotArrayMessage = "ESLint configuration error: \"plugins\" value must be an array";
+
+ debug(`${pluginNotArrayMessage}: ${JSON.stringify(pluginNames)}`);
+
+ throw new Error(pluginNotArrayMessage);
+ }
+
+ // load each plugin by name
+ pluginNames.forEach(this.load, this);
+ }
+}
+
+module.exports = Plugins;
diff --git a/tools/node_modules/eslint/lib/file-finder.js b/tools/node_modules/eslint/lib/file-finder.js
new file mode 100644
index 0000000000..3458bbf52a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/file-finder.js
@@ -0,0 +1,145 @@
+/**
+ * @fileoverview Util class to find config files.
+ * @author Aliaksei Shytkin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Get the entries for a directory. Including a try-catch may be detrimental to
+ * function performance, so move it out here a separate function.
+ * @param {string} directory The directory to search in.
+ * @returns {string[]} The entries in the directory or an empty array on error.
+ * @private
+ */
+function getDirectoryEntries(directory) {
+ try {
+
+ return fs.readdirSync(directory);
+ } catch (ex) {
+ return [];
+ }
+}
+
+/**
+ * Create a hash of filenames from a directory listing
+ * @param {string[]} entries Array of directory entries.
+ * @param {string} directory Path to a current directory.
+ * @param {string[]} supportedConfigs List of support filenames.
+ * @returns {Object} Hashmap of filenames
+ */
+function normalizeDirectoryEntries(entries, directory, supportedConfigs) {
+ const fileHash = {};
+
+ entries.forEach(entry => {
+ if (supportedConfigs.indexOf(entry) >= 0) {
+ const resolvedEntry = path.resolve(directory, entry);
+
+ if (fs.statSync(resolvedEntry).isFile()) {
+ fileHash[entry] = resolvedEntry;
+ }
+ }
+ });
+ return fileHash;
+}
+
+//------------------------------------------------------------------------------
+// API
+//------------------------------------------------------------------------------
+
+/**
+ * FileFinder class
+ */
+class FileFinder {
+
+ /**
+ * @param {string[]} files The basename(s) of the file(s) to find.
+ * @param {stirng} cwd Current working directory
+ */
+ constructor(files, cwd) {
+ this.fileNames = Array.isArray(files) ? files : [files];
+ this.cwd = cwd || process.cwd();
+ this.cache = {};
+ }
+
+ /**
+ * Find all instances of files with the specified file names, in directory and
+ * parent directories. Cache the results.
+ * Does not check if a matching directory entry is a file.
+ * Searches for all the file names in this.fileNames.
+ * Is currently used by lib/config.js to find .eslintrc and package.json files.
+ * @param {string} directory The directory to start the search from.
+ * @returns {GeneratorFunction} to iterate the file paths found
+ */
+ *findAllInDirectoryAndParents(directory) {
+ const cache = this.cache;
+
+ if (directory) {
+ directory = path.resolve(this.cwd, directory);
+ } else {
+ directory = this.cwd;
+ }
+
+ if (cache.hasOwnProperty(directory)) {
+ yield* cache[directory];
+ return; // to avoid doing the normal loop afterwards
+ }
+
+ const dirs = [];
+ const fileNames = this.fileNames;
+ let searched = 0;
+
+ do {
+ dirs[searched++] = directory;
+ cache[directory] = [];
+
+ const filesMap = normalizeDirectoryEntries(getDirectoryEntries(directory), directory, fileNames);
+
+ if (Object.keys(filesMap).length) {
+ for (let k = 0; k < fileNames.length; k++) {
+
+ if (filesMap[fileNames[k]]) {
+ const filePath = filesMap[fileNames[k]];
+
+ // Add the file path to the cache of each directory searched.
+ for (let j = 0; j < searched; j++) {
+ cache[dirs[j]].push(filePath);
+ }
+ yield filePath;
+ break;
+ }
+ }
+ }
+
+ const child = directory;
+
+ // Assign parent directory to directory.
+ directory = path.dirname(directory);
+
+ if (directory === child) {
+ return;
+ }
+
+ } while (!cache.hasOwnProperty(directory));
+
+ // Add what has been cached previously to the cache of each directory searched.
+ for (let i = 0; i < searched; i++) {
+ dirs.push.apply(cache[dirs[i]], cache[directory]);
+ }
+
+ yield* cache[dirs[0]];
+ }
+}
+
+module.exports = FileFinder;
diff --git a/tools/node_modules/eslint/lib/formatters/checkstyle.js b/tools/node_modules/eslint/lib/formatters/checkstyle.js
new file mode 100644
index 0000000000..720e831a3e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/checkstyle.js
@@ -0,0 +1,60 @@
+/**
+ * @fileoverview CheckStyle XML reporter
+ * @author Ian Christian Myers
+ */
+"use strict";
+
+const xmlEscape = require("../util/xml-escape");
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {Object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "error";
+ }
+ return "warning";
+
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "";
+
+ output += "<?xml version=\"1.1\" encoding=\"utf-8\"?>";
+ output += "<checkstyle version=\"4.3\">";
+
+ results.forEach(result => {
+ const messages = result.messages;
+
+ output += `<file name="${xmlEscape(result.filePath)}">`;
+
+ messages.forEach(message => {
+ output += [
+ `<error line="${xmlEscape(message.line)}"`,
+ `column="${xmlEscape(message.column)}"`,
+ `severity="${xmlEscape(getMessageType(message))}"`,
+ `message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`,
+ `source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />`
+ ].join(" ");
+ });
+
+ output += "</file>";
+
+ });
+
+ output += "</checkstyle>";
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/codeframe.js b/tools/node_modules/eslint/lib/formatters/codeframe.js
new file mode 100644
index 0000000000..0b97a0d818
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/codeframe.js
@@ -0,0 +1,138 @@
+/**
+ * @fileoverview Codeframe reporter
+ * @author Vitor Balocco
+ */
+"use strict";
+
+const chalk = require("chalk");
+const codeFrame = require("babel-code-frame");
+const path = require("path");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Given a word and a count, append an s if count is not one.
+ * @param {string} word A word in its singular form.
+ * @param {number} count A number controlling whether word should be pluralized.
+ * @returns {string} The original word with an s on the end if count is not one.
+ */
+function pluralize(word, count) {
+ return (count === 1 ? word : `${word}s`);
+}
+
+/**
+ * Gets a formatted relative file path from an absolute path and a line/column in the file.
+ * @param {string} filePath The absolute file path to format.
+ * @param {number} line The line from the file to use for formatting.
+ * @param {number} column The column from the file to use for formatting.
+ * @returns {string} The formatted file path.
+ */
+function formatFilePath(filePath, line, column) {
+ let relPath = path.relative(process.cwd(), filePath);
+
+ if (line && column) {
+ relPath += `:${line}:${column}`;
+ }
+
+ return chalk.green(relPath);
+}
+
+/**
+ * Gets the formatted output for a given message.
+ * @param {Object} message The object that represents this message.
+ * @param {Object} parentResult The result object that this message belongs to.
+ * @returns {string} The formatted output.
+ */
+function formatMessage(message, parentResult) {
+ const type = (message.fatal || message.severity === 2) ? chalk.red("error") : chalk.yellow("warning");
+ const msg = `${chalk.bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
+ const ruleId = message.fatal ? "" : chalk.dim(`(${message.ruleId})`);
+ const filePath = formatFilePath(parentResult.filePath, message.line, message.column);
+ const sourceCode = parentResult.output ? parentResult.output : parentResult.source;
+
+ const firstLine = [
+ `${type}:`,
+ `${msg}`,
+ ruleId ? `${ruleId}` : "",
+ sourceCode ? `at ${filePath}:` : `at ${filePath}`
+ ].filter(String).join(" ");
+
+ const result = [firstLine];
+
+ if (sourceCode) {
+ result.push(
+ codeFrame(sourceCode, message.line, message.column, { highlightCode: false })
+ );
+ }
+
+ return result.join("\n");
+}
+
+/**
+ * Gets the formatted output summary for a given number of errors and warnings.
+ * @param {number} errors The number of errors.
+ * @param {number} warnings The number of warnings.
+ * @param {number} fixableErrors The number of fixable errors.
+ * @param {number} fixableWarnings The number of fixable warnings.
+ * @returns {string} The formatted output summary.
+ */
+function formatSummary(errors, warnings, fixableErrors, fixableWarnings) {
+ const summaryColor = errors > 0 ? "red" : "yellow";
+ const summary = [];
+ const fixablesSummary = [];
+
+ if (errors > 0) {
+ summary.push(`${errors} ${pluralize("error", errors)}`);
+ }
+
+ if (warnings > 0) {
+ summary.push(`${warnings} ${pluralize("warning", warnings)}`);
+ }
+
+ if (fixableErrors > 0) {
+ fixablesSummary.push(`${fixableErrors} ${pluralize("error", fixableErrors)}`);
+ }
+
+ if (fixableWarnings > 0) {
+ fixablesSummary.push(`${fixableWarnings} ${pluralize("warning", fixableWarnings)}`);
+ }
+
+ let output = chalk[summaryColor].bold(`${summary.join(" and ")} found.`);
+
+ if (fixableErrors || fixableWarnings) {
+ output += chalk[summaryColor].bold(`\n${fixablesSummary.join(" and ")} potentially fixable with the \`--fix\` option.`);
+ }
+
+ return output;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+ let errors = 0;
+ let warnings = 0;
+ let fixableErrors = 0;
+ let fixableWarnings = 0;
+
+ const resultsWithMessages = results.filter(result => result.messages.length > 0);
+
+ let output = resultsWithMessages.reduce((resultsOutput, result) => {
+ const messages = result.messages.map(message => `${formatMessage(message, result)}\n\n`);
+
+ errors += result.errorCount;
+ warnings += result.warningCount;
+ fixableErrors += result.fixableErrorCount;
+ fixableWarnings += result.fixableWarningCount;
+
+ return resultsOutput.concat(messages);
+ }, []).join("\n");
+
+ output += "\n";
+ output += formatSummary(errors, warnings, fixableErrors, fixableWarnings);
+
+ return (errors + warnings) > 0 ? output : "";
+};
diff --git a/tools/node_modules/eslint/lib/formatters/compact.js b/tools/node_modules/eslint/lib/formatters/compact.js
new file mode 100644
index 0000000000..2b540bde23
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/compact.js
@@ -0,0 +1,60 @@
+/**
+ * @fileoverview Compact reporter
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {Object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "Error";
+ }
+ return "Warning";
+
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "",
+ total = 0;
+
+ results.forEach(result => {
+
+ const messages = result.messages;
+
+ total += messages.length;
+
+ messages.forEach(message => {
+
+ output += `${result.filePath}: `;
+ output += `line ${message.line || 0}`;
+ output += `, col ${message.column || 0}`;
+ output += `, ${getMessageType(message)}`;
+ output += ` - ${message.message}`;
+ output += message.ruleId ? ` (${message.ruleId})` : "";
+ output += "\n";
+
+ });
+
+ });
+
+ if (total > 0) {
+ output += `\n${total} problem${total !== 1 ? "s" : ""}`;
+ }
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/html-template-message.html b/tools/node_modules/eslint/lib/formatters/html-template-message.html
new file mode 100644
index 0000000000..66f49ff49d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/html-template-message.html
@@ -0,0 +1,8 @@
+<tr style="display:none" class="f-<%= parentIndex %>">
+ <td><%= lineNumber %>:<%= columnNumber %></td>
+ <td class="clr-<%= severityNumber %>"><%= severityName %></td>
+ <td><%- message %></td>
+ <td>
+ <a href="https://eslint.org/docs/rules/<%= ruleId %>" target="_blank"><%= ruleId %></a>
+ </td>
+</tr>
diff --git a/tools/node_modules/eslint/lib/formatters/html-template-page.html b/tools/node_modules/eslint/lib/formatters/html-template-page.html
new file mode 100644
index 0000000000..4016576fa0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/html-template-page.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>ESLint Report</title>
+ <style>
+ body {
+ font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
+ font-size:16px;
+ font-weight:normal;
+ margin:0;
+ padding:0;
+ color:#333
+ }
+ #overview {
+ padding:20px 30px
+ }
+ td, th {
+ padding:5px 10px
+ }
+ h1 {
+ margin:0
+ }
+ table {
+ margin:30px;
+ width:calc(100% - 60px);
+ max-width:1000px;
+ border-radius:5px;
+ border:1px solid #ddd;
+ border-spacing:0px;
+ }
+ th {
+ font-weight:400;
+ font-size:medium;
+ text-align:left;
+ cursor:pointer
+ }
+ td.clr-1, td.clr-2, th span {
+ font-weight:700
+ }
+ th span {
+ float:right;
+ margin-left:20px
+ }
+ th span:after {
+ content:"";
+ clear:both;
+ display:block
+ }
+ tr:last-child td {
+ border-bottom:none
+ }
+ tr td:first-child, tr td:last-child {
+ color:#9da0a4
+ }
+ #overview.bg-0, tr.bg-0 th {
+ color:#468847;
+ background:#dff0d8;
+ border-bottom:1px solid #d6e9c6
+ }
+ #overview.bg-1, tr.bg-1 th {
+ color:#f0ad4e;
+ background:#fcf8e3;
+ border-bottom:1px solid #fbeed5
+ }
+ #overview.bg-2, tr.bg-2 th {
+ color:#b94a48;
+ background:#f2dede;
+ border-bottom:1px solid #eed3d7
+ }
+ td {
+ border-bottom:1px solid #ddd
+ }
+ td.clr-1 {
+ color:#f0ad4e
+ }
+ td.clr-2 {
+ color:#b94a48
+ }
+ td a {
+ color:#3a33d1;
+ text-decoration:none
+ }
+ td a:hover {
+ color:#272296;
+ text-decoration:underline
+ }
+ </style>
+ </head>
+ <body>
+ <div id="overview" class="bg-<%= reportColor %>">
+ <h1>ESLint Report</h1>
+ <div>
+ <span><%= reportSummary %></span> - Generated on <%= date %>
+ </div>
+ </div>
+ <table>
+ <tbody>
+ <%= results %>
+ </tbody>
+ </table>
+ <script type="text/javascript">
+ var groups = document.querySelectorAll("tr[data-group]");
+ for (i = 0; i < groups.length; i++) {
+ groups[i].addEventListener("click", function() {
+ var inGroup = document.getElementsByClassName(this.getAttribute("data-group"));
+ this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+");
+ for (var j = 0; j < inGroup.length; j++) {
+ inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row";
+ }
+ });
+ }
+ </script>
+ </body>
+</html>
diff --git a/tools/node_modules/eslint/lib/formatters/html-template-result.html b/tools/node_modules/eslint/lib/formatters/html-template-result.html
new file mode 100644
index 0000000000..f4a55933c2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/html-template-result.html
@@ -0,0 +1,6 @@
+<tr class="bg-<%- color %>" data-group="f-<%- index %>">
+ <th colspan="4">
+ [+] <%- filePath %>
+ <span><%- summary %></span>
+ </th>
+</tr>
diff --git a/tools/node_modules/eslint/lib/formatters/html.js b/tools/node_modules/eslint/lib/formatters/html.js
new file mode 100644
index 0000000000..d450f9dee2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/html.js
@@ -0,0 +1,127 @@
+/**
+ * @fileoverview HTML reporter
+ * @author Julian Laval
+ */
+"use strict";
+
+const lodash = require("lodash");
+const fs = require("fs");
+const path = require("path");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const pageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-page.html"), "utf-8"));
+const messageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-message.html"), "utf-8"));
+const resultTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-result.html"), "utf-8"));
+
+/**
+ * Given a word and a count, append an s if count is not one.
+ * @param {string} word A word in its singular form.
+ * @param {int} count A number controlling whether word should be pluralized.
+ * @returns {string} The original word with an s on the end if count is not one.
+ */
+function pluralize(word, count) {
+ return (count === 1 ? word : `${word}s`);
+}
+
+/**
+ * Renders text along the template of x problems (x errors, x warnings)
+ * @param {string} totalErrors Total errors
+ * @param {string} totalWarnings Total warnings
+ * @returns {string} The formatted string, pluralized where necessary
+ */
+function renderSummary(totalErrors, totalWarnings) {
+ const totalProblems = totalErrors + totalWarnings;
+ let renderedText = `${totalProblems} ${pluralize("problem", totalProblems)}`;
+
+ if (totalProblems !== 0) {
+ renderedText += ` (${totalErrors} ${pluralize("error", totalErrors)}, ${totalWarnings} ${pluralize("warning", totalWarnings)})`;
+ }
+ return renderedText;
+}
+
+/**
+ * Get the color based on whether there are errors/warnings...
+ * @param {string} totalErrors Total errors
+ * @param {string} totalWarnings Total warnings
+ * @returns {int} The color code (0 = green, 1 = yellow, 2 = red)
+ */
+function renderColor(totalErrors, totalWarnings) {
+ if (totalErrors !== 0) {
+ return 2;
+ }
+ if (totalWarnings !== 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Get HTML (table rows) describing the messages.
+ * @param {Array} messages Messages.
+ * @param {int} parentIndex Index of the parent HTML row.
+ * @returns {string} HTML (table rows) describing the messages.
+ */
+function renderMessages(messages, parentIndex) {
+
+ /**
+ * Get HTML (table row) describing a message.
+ * @param {Object} message Message.
+ * @returns {string} HTML (table row) describing a message.
+ */
+ return lodash.map(messages, message => {
+ const lineNumber = message.line || 0;
+ const columnNumber = message.column || 0;
+
+ return messageTemplate({
+ parentIndex,
+ lineNumber,
+ columnNumber,
+ severityNumber: message.severity,
+ severityName: message.severity === 1 ? "Warning" : "Error",
+ message: message.message,
+ ruleId: message.ruleId
+ });
+ }).join("\n");
+}
+
+/**
+ * @param {Array} results Test results.
+ * @returns {string} HTML string describing the results.
+ */
+function renderResults(results) {
+ return lodash.map(results, (result, index) => resultTemplate({
+ index,
+ color: renderColor(result.errorCount, result.warningCount),
+ filePath: result.filePath,
+ summary: renderSummary(result.errorCount, result.warningCount)
+
+ }) + renderMessages(result.messages, index)).join("\n");
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+ let totalErrors,
+ totalWarnings;
+
+ totalErrors = 0;
+ totalWarnings = 0;
+
+ // Iterate over results to get totals
+ results.forEach(result => {
+ totalErrors += result.errorCount;
+ totalWarnings += result.warningCount;
+ });
+
+ return pageTemplate({
+ date: new Date(),
+ reportColor: renderColor(totalErrors, totalWarnings),
+ reportSummary: renderSummary(totalErrors, totalWarnings),
+ results: renderResults(results)
+ });
+};
diff --git a/tools/node_modules/eslint/lib/formatters/jslint-xml.js b/tools/node_modules/eslint/lib/formatters/jslint-xml.js
new file mode 100644
index 0000000000..e152d8bdd7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/jslint-xml.js
@@ -0,0 +1,41 @@
+/**
+ * @fileoverview JSLint XML reporter
+ * @author Ian Christian Myers
+ */
+"use strict";
+
+const xmlEscape = require("../util/xml-escape");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "";
+
+ output += "<?xml version=\"1.1\" encoding=\"utf-8\"?>";
+ output += "<jslint>";
+
+ results.forEach(result => {
+ const messages = result.messages;
+
+ output += `<file name="${result.filePath}">`;
+
+ messages.forEach(message => {
+ output += [
+ `<issue line="${message.line}"`,
+ `char="${message.column}"`,
+ `evidence="${xmlEscape(message.source || "")}"`,
+ `reason="${xmlEscape(message.message || "")}${message.ruleId ? ` (${message.ruleId})` : ""}" />`
+ ].join(" ");
+ });
+
+ output += "</file>";
+
+ });
+
+ output += "</jslint>";
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/json.js b/tools/node_modules/eslint/lib/formatters/json.js
new file mode 100644
index 0000000000..82138af187
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/json.js
@@ -0,0 +1,13 @@
+/**
+ * @fileoverview JSON reporter
+ * @author Burak Yigit Kaya aka BYK
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+ return JSON.stringify(results);
+};
diff --git a/tools/node_modules/eslint/lib/formatters/junit.js b/tools/node_modules/eslint/lib/formatters/junit.js
new file mode 100644
index 0000000000..ca666bb14c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/junit.js
@@ -0,0 +1,70 @@
+/**
+ * @fileoverview jUnit Reporter
+ * @author Jamund Ferguson
+ */
+"use strict";
+
+const xmlEscape = require("../util/xml-escape");
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {Object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "Error";
+ }
+ return "Warning";
+
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "";
+
+ output += "<?xml version=\"1.1\" encoding=\"utf-8\"?>\n";
+ output += "<testsuites>\n";
+
+ results.forEach(result => {
+
+ const messages = result.messages;
+
+ if (messages.length > 0) {
+ output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
+ messages.forEach(message => {
+ const type = message.fatal ? "error" : "failure";
+
+ output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}">`;
+ output += `<${type} message="${xmlEscape(message.message || "")}">`;
+ output += "<![CDATA[";
+ output += `line ${message.line || 0}, col `;
+ output += `${message.column || 0}, ${getMessageType(message)}`;
+ output += ` - ${xmlEscape(message.message || "")}`;
+ output += (message.ruleId ? ` (${message.ruleId})` : "");
+ output += "]]>";
+ output += `</${type}>`;
+ output += "</testcase>\n";
+ });
+ output += "</testsuite>\n";
+ } else {
+ output += `<testsuite package="org.eslint" time="0" tests="1" errors="0" name="${result.filePath}">\n`;
+ output += `<testcase time="0" name="${result.filePath}" />\n`;
+ output += "</testsuite>\n";
+ }
+
+ });
+
+ output += "</testsuites>\n";
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/stylish.js b/tools/node_modules/eslint/lib/formatters/stylish.js
new file mode 100644
index 0000000000..e586fe857c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/stylish.js
@@ -0,0 +1,100 @@
+/**
+ * @fileoverview Stylish reporter
+ * @author Sindre Sorhus
+ */
+"use strict";
+
+const chalk = require("chalk"),
+ stripAnsi = require("strip-ansi"),
+ table = require("text-table");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Given a word and a count, append an s if count is not one.
+ * @param {string} word A word in its singular form.
+ * @param {int} count A number controlling whether word should be pluralized.
+ * @returns {string} The original word with an s on the end if count is not one.
+ */
+function pluralize(word, count) {
+ return (count === 1 ? word : `${word}s`);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "\n",
+ errorCount = 0,
+ warningCount = 0,
+ fixableErrorCount = 0,
+ fixableWarningCount = 0,
+ summaryColor = "yellow";
+
+ results.forEach(result => {
+ const messages = result.messages;
+
+ if (messages.length === 0) {
+ return;
+ }
+
+ errorCount += result.errorCount;
+ warningCount += result.warningCount;
+ fixableErrorCount += result.fixableErrorCount;
+ fixableWarningCount += result.fixableWarningCount;
+
+ output += `${chalk.underline(result.filePath)}\n`;
+
+ output += `${table(
+ messages.map(message => {
+ let messageType;
+
+ if (message.fatal || message.severity === 2) {
+ messageType = chalk.red("error");
+ summaryColor = "red";
+ } else {
+ messageType = chalk.yellow("warning");
+ }
+
+ return [
+ "",
+ message.line || 0,
+ message.column || 0,
+ messageType,
+ message.message.replace(/([^ ])\.$/, "$1"),
+ chalk.dim(message.ruleId || "")
+ ];
+ }),
+ {
+ align: ["", "r", "l"],
+ stringLength(str) {
+ return stripAnsi(str).length;
+ }
+ }
+ ).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
+ });
+
+ const total = errorCount + warningCount;
+
+ if (total > 0) {
+ output += chalk[summaryColor].bold([
+ "\u2716 ", total, pluralize(" problem", total),
+ " (", errorCount, pluralize(" error", errorCount), ", ",
+ warningCount, pluralize(" warning", warningCount), ")\n"
+ ].join(""));
+
+ if (fixableErrorCount > 0 || fixableWarningCount > 0) {
+ output += chalk[summaryColor].bold([
+ " ", fixableErrorCount, pluralize(" error", fixableErrorCount), ", ",
+ fixableWarningCount, pluralize(" warning", fixableWarningCount),
+ " potentially fixable with the `--fix` option.\n"
+ ].join(""));
+ }
+ }
+
+ return total > 0 ? output : "";
+};
diff --git a/tools/node_modules/eslint/lib/formatters/table.js b/tools/node_modules/eslint/lib/formatters/table.js
new file mode 100644
index 0000000000..ebc3314e7a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/table.js
@@ -0,0 +1,150 @@
+/**
+ * @fileoverview "table reporter.
+ * @author Gajus Kuizinas <gajus@gajus.com>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const chalk = require("chalk"),
+ table = require("table").table,
+ pluralize = require("pluralize");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Draws text table.
+ * @param {Array<Object>} messages Error messages relating to a specific file.
+ * @returns {string} A text table.
+ */
+function drawTable(messages) {
+ const rows = [];
+
+ if (messages.length === 0) {
+ return "";
+ }
+
+ rows.push([
+ chalk.bold("Line"),
+ chalk.bold("Column"),
+ chalk.bold("Type"),
+ chalk.bold("Message"),
+ chalk.bold("Rule ID")
+ ]);
+
+ messages.forEach(message => {
+ let messageType;
+
+ if (message.fatal || message.severity === 2) {
+ messageType = chalk.red("error");
+ } else {
+ messageType = chalk.yellow("warning");
+ }
+
+ rows.push([
+ message.line || 0,
+ message.column || 0,
+ messageType,
+ message.message,
+ message.ruleId || ""
+ ]);
+ });
+
+ return table(rows, {
+ columns: {
+ 0: {
+ width: 8,
+ wrapWord: true
+ },
+ 1: {
+ width: 8,
+ wrapWord: true
+ },
+ 2: {
+ width: 8,
+ wrapWord: true
+ },
+ 3: {
+ paddingRight: 5,
+ width: 50,
+ wrapWord: true
+ },
+ 4: {
+ width: 20,
+ wrapWord: true
+ }
+ },
+ drawHorizontalLine(index) {
+ return index === 1;
+ }
+ });
+}
+
+/**
+ * Draws a report (multiple tables).
+ * @param {Array} results Report results for every file.
+ * @returns {string} A column of text tables.
+ */
+function drawReport(results) {
+ let files;
+
+ files = results.map(result => {
+ if (!result.messages.length) {
+ return "";
+ }
+
+ return `\n${result.filePath}\n\n${drawTable(result.messages)}`;
+ });
+
+ files = files.filter(content => content.trim());
+
+ return files.join("");
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(report) {
+ let result,
+ errorCount,
+ warningCount;
+
+ result = "";
+ errorCount = 0;
+ warningCount = 0;
+
+ report.forEach(fileReport => {
+ errorCount += fileReport.errorCount;
+ warningCount += fileReport.warningCount;
+ });
+
+ if (errorCount || warningCount) {
+ result = drawReport(report);
+ }
+
+ result += `\n${table([
+ [
+ chalk.red(pluralize("Error", errorCount, true))
+ ],
+ [
+ chalk.yellow(pluralize("Warning", warningCount, true))
+ ]
+ ], {
+ columns: {
+ 0: {
+ width: 110,
+ wrapWord: true
+ }
+ },
+ drawHorizontalLine() {
+ return true;
+ }
+ })}`;
+
+ return result;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/tap.js b/tools/node_modules/eslint/lib/formatters/tap.js
new file mode 100644
index 0000000000..9651a2bcf1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/tap.js
@@ -0,0 +1,92 @@
+/**
+ * @fileoverview TAP reporter
+ * @author Jonathan Kingston
+ */
+"use strict";
+
+const yaml = require("js-yaml");
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns a canonical error level string based upon the error message passed in.
+ * @param {Object} message Individual error message provided by eslint
+ * @returns {string} Error level string
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "error";
+ }
+ return "warning";
+
+}
+
+/**
+ * Takes in a JavaScript object and outputs a TAP diagnostics string
+ * @param {Object} diagnostic JavaScript object to be embedded as YAML into output.
+ * @returns {string} diagnostics string with YAML embedded - TAP version 13 compliant
+ */
+function outputDiagnostics(diagnostic) {
+ const prefix = " ";
+ let output = `${prefix}---\n`;
+
+ output += prefix + yaml.safeDump(diagnostic).split("\n").join(`\n${prefix}`);
+ output += "...\n";
+ return output;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+ let output = `TAP version 13\n1..${results.length}\n`;
+
+ results.forEach((result, id) => {
+ const messages = result.messages;
+ let testResult = "ok";
+ let diagnostics = {};
+
+ if (messages.length > 0) {
+ testResult = "not ok";
+
+ messages.forEach(message => {
+ const diagnostic = {
+ message: message.message,
+ severity: getMessageType(message),
+ data: {
+ line: message.line || 0,
+ column: message.column || 0,
+ ruleId: message.ruleId || ""
+ }
+ };
+
+ /*
+ * If we have multiple messages place them under a messages key
+ * The first error will be logged as message key
+ * This is to adhere to TAP 13 loosely defined specification of having a message key
+ */
+ if ("message" in diagnostics) {
+ if (typeof diagnostics.messages === "undefined") {
+ diagnostics.messages = [];
+ }
+ diagnostics.messages.push(diagnostic);
+ } else {
+ diagnostics = diagnostic;
+ }
+ });
+ }
+
+ output += `${testResult} ${id + 1} - ${result.filePath}\n`;
+
+ // If we have an error include diagnostics
+ if (messages.length > 0) {
+ output += outputDiagnostics(diagnostics);
+ }
+
+ });
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/unix.js b/tools/node_modules/eslint/lib/formatters/unix.js
new file mode 100644
index 0000000000..c6c4ebbdb9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/unix.js
@@ -0,0 +1,58 @@
+/**
+ * @fileoverview unix-style formatter.
+ * @author oshi-shinobu
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns a canonical error level string based upon the error message passed in.
+ * @param {Object} message Individual error message provided by eslint
+ * @returns {string} Error level string
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "Error";
+ }
+ return "Warning";
+
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "",
+ total = 0;
+
+ results.forEach(result => {
+
+ const messages = result.messages;
+
+ total += messages.length;
+
+ messages.forEach(message => {
+
+ output += `${result.filePath}:`;
+ output += `${message.line || 0}:`;
+ output += `${message.column || 0}:`;
+ output += ` ${message.message} `;
+ output += `[${getMessageType(message)}${message.ruleId ? `/${message.ruleId}` : ""}]`;
+ output += "\n";
+
+ });
+
+ });
+
+ if (total > 0) {
+ output += `\n${total} problem${total !== 1 ? "s" : ""}`;
+ }
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/formatters/visualstudio.js b/tools/node_modules/eslint/lib/formatters/visualstudio.js
new file mode 100644
index 0000000000..0d49431db8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/formatters/visualstudio.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Visual Studio compatible formatter
+ * @author Ronald Pijnacker
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {Object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "error";
+ }
+ return "warning";
+
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ let output = "",
+ total = 0;
+
+ results.forEach(result => {
+
+ const messages = result.messages;
+
+ total += messages.length;
+
+ messages.forEach(message => {
+
+ output += result.filePath;
+ output += `(${message.line || 0}`;
+ output += message.column ? `,${message.column}` : "";
+ output += `): ${getMessageType(message)}`;
+ output += message.ruleId ? ` ${message.ruleId}` : "";
+ output += ` : ${message.message}`;
+ output += "\n";
+
+ });
+
+ });
+
+ if (total === 0) {
+ output += "no problems";
+ } else {
+ output += `\n${total} problem${total !== 1 ? "s" : ""}`;
+ }
+
+ return output;
+};
diff --git a/tools/node_modules/eslint/lib/ignored-paths.js b/tools/node_modules/eslint/lib/ignored-paths.js
new file mode 100644
index 0000000000..c02e83bc2a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/ignored-paths.js
@@ -0,0 +1,289 @@
+/**
+ * @fileoverview Responsible for loading ignore config files and managing ignore patterns
+ * @author Jonathan Rajavuori
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path"),
+ ignore = require("ignore"),
+ pathUtil = require("./util/path-util");
+
+const debug = require("debug")("eslint:ignored-paths");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const ESLINT_IGNORE_FILENAME = ".eslintignore";
+
+/**
+ * Adds `"*"` at the end of `"node_modules/"`,
+ * so that subtle directories could be re-included by .gitignore patterns
+ * such as `"!node_modules/should_not_ignored"`
+ */
+const DEFAULT_IGNORE_DIRS = [
+ "/node_modules/*",
+ "/bower_components/*"
+];
+const DEFAULT_OPTIONS = {
+ dotfiles: false,
+ cwd: process.cwd()
+};
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Find a file in the current directory.
+ * @param {string} cwd Current working directory
+ * @param {string} name File name
+ * @returns {string} Path of ignore file or an empty string.
+ */
+function findFile(cwd, name) {
+ const ignoreFilePath = path.resolve(cwd, name);
+
+ return fs.existsSync(ignoreFilePath) && fs.statSync(ignoreFilePath).isFile() ? ignoreFilePath : "";
+}
+
+/**
+ * Find an ignore file in the current directory.
+ * @param {string} cwd Current working directory
+ * @returns {string} Path of ignore file or an empty string.
+ */
+function findIgnoreFile(cwd) {
+ return findFile(cwd, ESLINT_IGNORE_FILENAME);
+}
+
+/**
+ * Find an package.json file in the current directory.
+ * @param {string} cwd Current working directory
+ * @returns {string} Path of package.json file or an empty string.
+ */
+function findPackageJSONFile(cwd) {
+ return findFile(cwd, "package.json");
+}
+
+/**
+ * Merge options with defaults
+ * @param {Object} options Options to merge with DEFAULT_OPTIONS constant
+ * @returns {Object} Merged options
+ */
+function mergeDefaultOptions(options) {
+ options = (options || {});
+ return Object.assign({}, DEFAULT_OPTIONS, options);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * IgnoredPaths class
+ */
+class IgnoredPaths {
+
+ /**
+ * @param {Object} options object containing 'ignore', 'ignorePath' and 'patterns' properties
+ */
+ constructor(options) {
+ options = mergeDefaultOptions(options);
+ this.cache = {};
+
+ /**
+ * add pattern to node-ignore instance
+ * @param {Object} ig, instance of node-ignore
+ * @param {string} pattern, pattern do add to ig
+ * @returns {array} raw ignore rules
+ */
+ function addPattern(ig, pattern) {
+ return ig.addPattern(pattern);
+ }
+
+ this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []);
+ this.baseDir = options.cwd;
+
+ this.ig = {
+ custom: ignore(),
+ default: ignore()
+ };
+
+ /*
+ * Add a way to keep track of ignored files. This was present in node-ignore
+ * 2.x, but dropped for now as of 3.0.10.
+ */
+ this.ig.custom.ignoreFiles = [];
+ this.ig.default.ignoreFiles = [];
+
+ if (options.dotfiles !== true) {
+
+ /*
+ * ignore files beginning with a dot, but not files in a parent or
+ * ancestor directory (which in relative format will begin with `../`).
+ */
+ addPattern(this.ig.default, [".*", "!../"]);
+ }
+
+ addPattern(this.ig.default, this.defaultPatterns);
+
+ if (options.ignore !== false) {
+ let ignorePath;
+
+ if (options.ignorePath) {
+ debug("Using specific ignore file");
+
+ try {
+ fs.statSync(options.ignorePath);
+ ignorePath = options.ignorePath;
+ } catch (e) {
+ e.message = `Cannot read ignore file: ${options.ignorePath}\nError: ${e.message}`;
+ throw e;
+ }
+ } else {
+ debug(`Looking for ignore file in ${options.cwd}`);
+ ignorePath = findIgnoreFile(options.cwd);
+
+ try {
+ fs.statSync(ignorePath);
+ debug(`Loaded ignore file ${ignorePath}`);
+ } catch (e) {
+ debug("Could not find ignore file in cwd");
+ this.options = options;
+ }
+ }
+
+ if (ignorePath) {
+ debug(`Adding ${ignorePath}`);
+ this.baseDir = path.dirname(path.resolve(options.cwd, ignorePath));
+ this.addIgnoreFile(this.ig.custom, ignorePath);
+ this.addIgnoreFile(this.ig.default, ignorePath);
+ } else {
+ try {
+
+ // if the ignoreFile does not exist, check package.json for eslintIgnore
+ const packageJSONPath = findPackageJSONFile(options.cwd);
+
+ if (packageJSONPath) {
+ let packageJSONOptions;
+
+ try {
+ packageJSONOptions = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"));
+ } catch (e) {
+ debug("Could not read package.json file to check eslintIgnore property");
+ throw e;
+ }
+
+ if (packageJSONOptions.eslintIgnore) {
+ if (Array.isArray(packageJSONOptions.eslintIgnore)) {
+ packageJSONOptions.eslintIgnore.forEach(pattern => {
+ addPattern(this.ig.custom, pattern);
+ addPattern(this.ig.default, pattern);
+ });
+ } else {
+ throw new TypeError("Package.json eslintIgnore property requires an array of paths");
+ }
+ }
+ }
+ } catch (e) {
+ debug("Could not find package.json to check eslintIgnore property");
+ throw e;
+ }
+ }
+
+ if (options.ignorePattern) {
+ addPattern(this.ig.custom, options.ignorePattern);
+ addPattern(this.ig.default, options.ignorePattern);
+ }
+ }
+
+ this.options = options;
+ }
+
+ /**
+ * read ignore filepath
+ * @param {string} filePath, file to add to ig
+ * @returns {array} raw ignore rules
+ */
+ readIgnoreFile(filePath) {
+ if (typeof this.cache[filePath] === "undefined") {
+ this.cache[filePath] = fs.readFileSync(filePath, "utf8");
+ }
+ return this.cache[filePath];
+ }
+
+ /**
+ * add ignore file to node-ignore instance
+ * @param {Object} ig, instance of node-ignore
+ * @param {string} filePath, file to add to ig
+ * @returns {array} raw ignore rules
+ */
+ addIgnoreFile(ig, filePath) {
+ ig.ignoreFiles.push(filePath);
+ return ig.add(this.readIgnoreFile(filePath));
+ }
+
+ /**
+ * Determine whether a file path is included in the default or custom ignore patterns
+ * @param {string} filepath Path to check
+ * @param {string} [category=null] check 'default', 'custom' or both (null)
+ * @returns {boolean} true if the file path matches one or more patterns, false otherwise
+ */
+ contains(filepath, category) {
+
+ let result = false;
+ const absolutePath = path.resolve(this.options.cwd, filepath);
+ const relativePath = pathUtil.getRelativePath(absolutePath, this.baseDir);
+
+ if ((typeof category === "undefined") || (category === "default")) {
+ result = result || (this.ig.default.filter([relativePath]).length === 0);
+ }
+
+ if ((typeof category === "undefined") || (category === "custom")) {
+ result = result || (this.ig.custom.filter([relativePath]).length === 0);
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Returns a list of dir patterns for glob to ignore
+ * @returns {function()} method to check whether a folder should be ignored by glob.
+ */
+ getIgnoredFoldersGlobChecker() {
+
+ const ig = ignore().add(DEFAULT_IGNORE_DIRS);
+
+ if (this.options.dotfiles !== true) {
+
+ // Ignore hidden folders. (This cannot be ".*", or else it's not possible to unignore hidden files)
+ ig.add([".*/*", "!../"]);
+ }
+
+ if (this.options.ignore) {
+ ig.add(this.ig.custom);
+ }
+
+ const filter = ig.createFilter();
+
+ const base = this.baseDir;
+
+ return function(absolutePath) {
+ const relative = pathUtil.getRelativePath(absolutePath, base);
+
+ if (!relative) {
+ return false;
+ }
+
+ return !filter(relative);
+ };
+ }
+}
+
+module.exports = IgnoredPaths;
diff --git a/tools/node_modules/eslint/lib/linter.js b/tools/node_modules/eslint/lib/linter.js
new file mode 100755
index 0000000000..21d62f73ae
--- /dev/null
+++ b/tools/node_modules/eslint/lib/linter.js
@@ -0,0 +1,1123 @@
+/**
+ * @fileoverview Main Linter Class
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const eslintScope = require("eslint-scope"),
+ levn = require("levn"),
+ lodash = require("lodash"),
+ blankScriptAST = require("../conf/blank-script.json"),
+ defaultConfig = require("../conf/default-config-options.js"),
+ CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
+ ConfigOps = require("./config/config-ops"),
+ validator = require("./config/config-validator"),
+ Environments = require("./config/environments"),
+ applyDisableDirectives = require("./util/apply-disable-directives"),
+ createEmitter = require("./util/safe-emitter"),
+ NodeEventGenerator = require("./util/node-event-generator"),
+ SourceCode = require("./util/source-code"),
+ Traverser = require("./util/traverser"),
+ createReportTranslator = require("./report-translator"),
+ Rules = require("./rules"),
+ timing = require("./timing"),
+ astUtils = require("./ast-utils"),
+ pkg = require("../package.json"),
+ SourceCodeFixer = require("./util/source-code-fixer");
+
+const debug = require("debug")("eslint:linter");
+const MAX_AUTOFIX_PASSES = 10;
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * The result of a parsing operation from parseForESLint()
+ * @typedef {Object} CustomParseResult
+ * @property {ASTNode} ast The ESTree AST Program node.
+ * @property {Object} services An object containing additional services related
+ * to the parser.
+ */
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
+ * whitespace.
+ * @param {string} string The string to parse.
+ * @param {Comment} comment The comment node which has the string.
+ * @returns {Object} Result map object of names and boolean values
+ */
+function parseBooleanConfig(string, comment) {
+ const items = {};
+
+ // Collapse whitespace around `:` and `,` to make parsing easier
+ string = string.replace(/\s*([:,])\s*/g, "$1");
+
+ string.split(/\s|,+/).forEach(name => {
+ if (!name) {
+ return;
+ }
+ const pos = name.indexOf(":");
+ let value;
+
+ if (pos !== -1) {
+ value = name.slice(pos + 1);
+ name = name.slice(0, pos);
+ }
+
+ items[name] = {
+ value: (value === "true"),
+ comment
+ };
+
+ });
+ return items;
+}
+
+/**
+ * Parses a JSON-like config.
+ * @param {string} string The string to parse.
+ * @param {Object} location Start line and column of comments for potential error message.
+ * @returns {({success: true, config: Object}|{success: false, error: Problem})} Result map object
+ */
+function parseJsonConfig(string, location) {
+ let items = {};
+
+ // Parses a JSON-like comment by the same way as parsing CLI option.
+ try {
+ items = levn.parse("Object", string) || {};
+
+ // Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc*/`.
+ // Also, commaless notations have invalid severity:
+ // "no-alert: 2 no-console: 2" --> {"no-alert": "2 no-console: 2"}
+ // Should ignore that case as well.
+ if (ConfigOps.isEverySeverityValid(items)) {
+ return {
+ success: true,
+ config: items
+ };
+ }
+ } catch (ex) {
+
+ // ignore to parse the string by a fallback.
+ }
+
+ /*
+ * Optionator cannot parse commaless notations.
+ * But we are supporting that. So this is a fallback for that.
+ */
+ items = {};
+ string = string.replace(/([a-zA-Z0-9\-/]+):/g, "\"$1\":").replace(/(]|[0-9])\s+(?=")/, "$1,");
+ try {
+ items = JSON.parse(`{${string}}`);
+ } catch (ex) {
+ return {
+ success: false,
+ error: {
+ ruleId: null,
+ fatal: true,
+ severity: 2,
+ source: null,
+ message: `Failed to parse JSON from '${string}': ${ex.message}`,
+ line: location.start.line,
+ column: location.start.column + 1
+ }
+ };
+
+ }
+
+ return {
+ success: true,
+ config: items
+ };
+}
+
+/**
+ * Parses a config of values separated by comma.
+ * @param {string} string The string to parse.
+ * @returns {Object} Result map of values and true values
+ */
+function parseListConfig(string) {
+ const items = {};
+
+ // Collapse whitespace around ,
+ string = string.replace(/\s*,\s*/g, ",");
+
+ string.split(/,+/).forEach(name => {
+ name = name.trim();
+ if (!name) {
+ return;
+ }
+ items[name] = true;
+ });
+ return items;
+}
+
+/**
+ * Ensures that variables representing built-in properties of the Global Object,
+ * and any globals declared by special block comments, are present in the global
+ * scope.
+ * @param {Scope} globalScope The global scope.
+ * @param {Object} config The existing configuration data.
+ * @param {Environments} envContext Env context
+ * @returns {void}
+ */
+function addDeclaredGlobals(globalScope, config, envContext) {
+ const declaredGlobals = {},
+ exportedGlobals = {},
+ explicitGlobals = {},
+ builtin = envContext.get("builtin");
+
+ Object.assign(declaredGlobals, builtin);
+
+ Object.keys(config.env).filter(name => config.env[name]).forEach(name => {
+ const env = envContext.get(name),
+ environmentGlobals = env && env.globals;
+
+ if (environmentGlobals) {
+ Object.assign(declaredGlobals, environmentGlobals);
+ }
+ });
+
+ Object.assign(exportedGlobals, config.exported);
+ Object.assign(declaredGlobals, config.globals);
+ Object.assign(explicitGlobals, config.astGlobals);
+
+ Object.keys(declaredGlobals).forEach(name => {
+ let variable = globalScope.set.get(name);
+
+ if (!variable) {
+ variable = new eslintScope.Variable(name, globalScope);
+ variable.eslintExplicitGlobal = false;
+ globalScope.variables.push(variable);
+ globalScope.set.set(name, variable);
+ }
+ variable.writeable = declaredGlobals[name];
+ });
+
+ Object.keys(explicitGlobals).forEach(name => {
+ let variable = globalScope.set.get(name);
+
+ if (!variable) {
+ variable = new eslintScope.Variable(name, globalScope);
+ variable.eslintExplicitGlobal = true;
+ variable.eslintExplicitGlobalComment = explicitGlobals[name].comment;
+ globalScope.variables.push(variable);
+ globalScope.set.set(name, variable);
+ }
+ variable.writeable = explicitGlobals[name].value;
+ });
+
+ // mark all exported variables as such
+ Object.keys(exportedGlobals).forEach(name => {
+ const variable = globalScope.set.get(name);
+
+ if (variable) {
+ variable.eslintUsed = true;
+ }
+ });
+
+ /*
+ * "through" contains all references which definitions cannot be found.
+ * Since we augment the global scope using configuration, we need to update
+ * references and remove the ones that were added by configuration.
+ */
+ globalScope.through = globalScope.through.filter(reference => {
+ const name = reference.identifier.name;
+ const variable = globalScope.set.get(name);
+
+ if (variable) {
+
+ /*
+ * Links the variable and the reference.
+ * And this reference is removed from `Scope#through`.
+ */
+ reference.resolved = variable;
+ variable.references.push(reference);
+
+ return false;
+ }
+
+ return true;
+ });
+}
+
+/**
+ * Creates a collection of disable directives from a comment
+ * @param {("disable"|"enable"|"disable-line"|"disable-next-line")} type The type of directive comment
+ * @param {{line: number, column: number}} loc The 0-based location of the comment token
+ * @param {string} value The value after the directive in the comment
+ * comment specified no specific rules, so it applies to all rules (e.g. `eslint-disable`)
+ * @returns {{
+ * type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
+ * line: number,
+ * column: number,
+ * ruleId: (string|null)
+ * }[]} Directives from the comment
+ */
+function createDisableDirectives(type, loc, value) {
+ const ruleIds = Object.keys(parseListConfig(value));
+ const directiveRules = ruleIds.length ? ruleIds : [null];
+
+ return directiveRules.map(ruleId => ({ type, line: loc.line, column: loc.column + 1, ruleId }));
+}
+
+/**
+ * Parses comments in file to extract file-specific config of rules, globals
+ * and environments and merges them with global config; also code blocks
+ * where reporting is disabled or enabled and merges them with reporting config.
+ * @param {string} filename The file being checked.
+ * @param {ASTNode} ast The top node of the AST.
+ * @param {Object} config The existing configuration data.
+ * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
+ * @returns {{
+ * config: Object,
+ * problems: Problem[],
+ * disableDirectives: {
+ * type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
+ * line: number,
+ * column: number,
+ * ruleId: (string|null)
+ * }[]
+ * }} Modified config object, along with any problems encountered
+ * while parsing config comments
+ */
+function modifyConfigsFromComments(filename, ast, config, ruleMapper) {
+
+ const commentConfig = {
+ exported: {},
+ astGlobals: {},
+ rules: {},
+ env: {}
+ };
+ const commentRules = {};
+ const problems = [];
+ const disableDirectives = [];
+
+ ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
+
+ let value = comment.value.trim();
+ const match = /^(eslint(-\w+){0,3}|exported|globals?)(\s|$)/.exec(value);
+
+ if (match) {
+ value = value.slice(match.index + match[1].length);
+
+ if (comment.type === "Block") {
+ switch (match[1]) {
+ case "exported":
+ Object.assign(commentConfig.exported, parseBooleanConfig(value, comment));
+ break;
+
+ case "globals":
+ case "global":
+ Object.assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
+ break;
+
+ case "eslint-disable":
+ [].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
+ break;
+
+ case "eslint-enable":
+ [].push.apply(disableDirectives, createDisableDirectives("enable", comment.loc.start, value));
+ break;
+
+ case "eslint": {
+ const parseResult = parseJsonConfig(value, comment.loc);
+
+ if (parseResult.success) {
+ Object.keys(parseResult.config).forEach(name => {
+ const ruleValue = parseResult.config[name];
+
+ validator.validateRuleOptions(ruleMapper(name), name, ruleValue, `${filename} line ${comment.loc.start.line}`);
+ commentRules[name] = ruleValue;
+ });
+ } else {
+ problems.push(parseResult.error);
+ }
+
+ break;
+ }
+
+ // no default
+ }
+ } else { // comment.type === "Line"
+ if (match[1] === "eslint-disable-line") {
+ [].push.apply(disableDirectives, createDisableDirectives("disable-line", comment.loc.start, value));
+ } else if (match[1] === "eslint-disable-next-line") {
+ [].push.apply(disableDirectives, createDisableDirectives("disable-next-line", comment.loc.start, value));
+ }
+ }
+ }
+ });
+
+ Object.assign(commentConfig.rules, commentRules);
+
+ return {
+ config: ConfigOps.merge(config, commentConfig),
+ problems,
+ disableDirectives
+ };
+}
+
+/**
+ * Normalize ECMAScript version from the initial config
+ * @param {number} ecmaVersion ECMAScript version from the initial config
+ * @param {boolean} isModule Whether the source type is module or not
+ * @returns {number} normalized ECMAScript version
+ */
+function normalizeEcmaVersion(ecmaVersion, isModule) {
+
+ // Need at least ES6 for modules
+ if (isModule && (!ecmaVersion || ecmaVersion < 6)) {
+ ecmaVersion = 6;
+ }
+
+ /*
+ * Calculate ECMAScript edition number from official year version starting with
+ * ES2015, which corresponds with ES6 (or a difference of 2009).
+ */
+ if (ecmaVersion >= 2015) {
+ ecmaVersion -= 2009;
+ }
+
+ return ecmaVersion;
+}
+
+/**
+ * Process initial config to make it safe to extend by file comment config
+ * @param {Object} config Initial config
+ * @param {Environments} envContext Env context
+ * @returns {Object} Processed config
+ */
+function prepareConfig(config, envContext) {
+ config.globals = config.globals || {};
+ const copiedRules = {};
+ let parserOptions = {};
+
+ if (typeof config.rules === "object") {
+ Object.keys(config.rules).forEach(k => {
+ const rule = config.rules[k];
+
+ if (rule === null) {
+ throw new Error(`Invalid config for rule '${k}'.`);
+ }
+ if (Array.isArray(rule)) {
+ copiedRules[k] = rule.slice();
+ } else {
+ copiedRules[k] = rule;
+ }
+ });
+ }
+
+ // merge in environment parserOptions
+ if (typeof config.env === "object") {
+ Object.keys(config.env).forEach(envName => {
+ const env = envContext.get(envName);
+
+ if (config.env[envName] && env && env.parserOptions) {
+ parserOptions = ConfigOps.merge(parserOptions, env.parserOptions);
+ }
+ });
+ }
+
+ const preparedConfig = {
+ rules: copiedRules,
+ parser: config.parser || defaultConfig.parser,
+ globals: ConfigOps.merge(defaultConfig.globals, config.globals),
+ env: ConfigOps.merge(defaultConfig.env, config.env || {}),
+ settings: ConfigOps.merge(defaultConfig.settings, config.settings || {}),
+ parserOptions: ConfigOps.merge(parserOptions, config.parserOptions || {})
+ };
+ const isModule = preparedConfig.parserOptions.sourceType === "module";
+
+ if (isModule) {
+
+ // can't have global return inside of modules
+ preparedConfig.parserOptions.ecmaFeatures = Object.assign({}, preparedConfig.parserOptions.ecmaFeatures, { globalReturn: false });
+ }
+
+ preparedConfig.parserOptions.ecmaVersion = normalizeEcmaVersion(preparedConfig.parserOptions.ecmaVersion, isModule);
+
+ return preparedConfig;
+}
+
+const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//g;
+
+/**
+ * Checks whether or not there is a comment which has "eslint-env *" in a given text.
+ * @param {string} text - A source code text to check.
+ * @returns {Object|null} A result of parseListConfig() with "eslint-env *" comment.
+ */
+function findEslintEnv(text) {
+ let match, retv;
+
+ eslintEnvPattern.lastIndex = 0;
+
+ while ((match = eslintEnvPattern.exec(text))) {
+ retv = Object.assign(retv || {}, parseListConfig(match[1]));
+ }
+
+ return retv;
+}
+
+/**
+ * Strips Unicode BOM from a given text.
+ *
+ * @param {string} text - A text to strip.
+ * @returns {string} The stripped text.
+ */
+function stripUnicodeBOM(text) {
+
+ /*
+ * Check Unicode BOM.
+ * In JavaScript, string data is stored as UTF-16, so BOM is 0xFEFF.
+ * http://www.ecma-international.org/ecma-262/6.0/#sec-unicode-format-control-characters
+ */
+ if (text.charCodeAt(0) === 0xFEFF) {
+ return text.slice(1);
+ }
+ return text;
+}
+
+/**
+ * Get the options for a rule (not including severity), if any
+ * @param {Array|number} ruleConfig rule configuration
+ * @returns {Array} of rule options, empty Array if none
+ */
+function getRuleOptions(ruleConfig) {
+ if (Array.isArray(ruleConfig)) {
+ return ruleConfig.slice(1);
+ }
+ return [];
+
+}
+
+/**
+ * Parses text into an AST. Moved out here because the try-catch prevents
+ * optimization of functions, so it's best to keep the try-catch as isolated
+ * as possible
+ * @param {string} text The text to parse.
+ * @param {Object} providedParserOptions Options to pass to the parser
+ * @param {Object} parser The parser module
+ * @param {string} filePath The path to the file being parsed.
+ * @returns {{success: false, error: Problem}|{success: true,ast: ASTNode, services: Object}}
+ * An object containing the AST and parser services if parsing was successful, or the error if parsing failed
+ * @private
+ */
+function parse(text, providedParserOptions, parser, filePath) {
+
+ const parserOptions = Object.assign({}, providedParserOptions, {
+ loc: true,
+ range: true,
+ raw: true,
+ tokens: true,
+ comment: true,
+ filePath
+ });
+
+ /*
+ * Check for parsing errors first. If there's a parsing error, nothing
+ * else can happen. However, a parsing error does not throw an error
+ * from this method - it's just considered a fatal error message, a
+ * problem that ESLint identified just like any other.
+ */
+ try {
+ if (typeof parser.parseForESLint === "function") {
+ const parseResult = parser.parseForESLint(text, parserOptions);
+
+ return {
+ success: true,
+ ast: parseResult.ast,
+ services: parseResult.services || {}
+ };
+ }
+
+ return {
+ success: true,
+ ast: parser.parse(text, parserOptions),
+ services: {}
+ };
+ } catch (ex) {
+
+ // If the message includes a leading line number, strip it:
+ const message = `Parsing error: ${ex.message.replace(/^line \d+:/i, "").trim()}`;
+ const source = ex.lineNumber ? SourceCode.splitLines(text)[ex.lineNumber - 1] : null;
+
+ return {
+ success: false,
+ error: {
+ ruleId: null,
+ fatal: true,
+ severity: 2,
+ source,
+ message,
+ line: ex.lineNumber,
+ column: ex.column
+ }
+ };
+ }
+}
+
+/**
+ * Gets the scope for the current node
+ * @param {ScopeManager} scopeManager The scope manager for this AST
+ * @param {ASTNode} currentNode The node to get the scope of
+ * @param {number} ecmaVersion The `ecmaVersion` setting that this code was parsed with
+ * @returns {eslint-scope.Scope} The scope information for this node
+ */
+function getScope(scopeManager, currentNode, ecmaVersion) {
+ let initialNode;
+
+ // if current node introduces a scope, add it to the list
+ if (
+ ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(currentNode.type) >= 0 ||
+ ecmaVersion >= 6 && ["BlockStatement", "SwitchStatement", "CatchClause"].indexOf(currentNode.type) >= 0
+ ) {
+ initialNode = currentNode;
+ } else {
+ initialNode = currentNode.parent;
+ }
+
+ // Ascend the current node's parents
+ for (let node = initialNode; node; node = node.parent) {
+
+ // Get the innermost scope
+ const scope = scopeManager.acquire(node, true);
+
+ if (scope) {
+ if (scope.type === "function-expression-name") {
+ return scope.childScopes[0];
+ }
+ return scope;
+ }
+ }
+
+ return scopeManager.scopes[0];
+}
+
+/**
+ * Marks a variable as used in the current scope
+ * @param {ScopeManager} scopeManager The scope manager for this AST. The scope may be mutated by this function.
+ * @param {ASTNode} currentNode The node currently being traversed
+ * @param {Object} parserOptions The options used to parse this text
+ * @param {string} name The name of the variable that should be marked as used.
+ * @returns {boolean} True if the variable was found and marked as used, false if not.
+ */
+function markVariableAsUsed(scopeManager, currentNode, parserOptions, name) {
+ const hasGlobalReturn = parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn;
+ const specialScope = hasGlobalReturn || parserOptions.sourceType === "module";
+ const currentScope = getScope(scopeManager, currentNode, parserOptions.ecmaVersion);
+
+ // Special Node.js scope means we need to start one level deeper
+ const initialScope = currentScope.type === "global" && specialScope ? currentScope.childScopes[0] : currentScope;
+
+ for (let scope = initialScope; scope; scope = scope.upper) {
+ const variable = scope.variables.find(scopeVar => scopeVar.name === name);
+
+ if (variable) {
+ variable.eslintUsed = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// methods that exist on SourceCode object
+const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
+ getSource: "getText",
+ getSourceLines: "getLines",
+ getAllComments: "getAllComments",
+ getNodeByRangeIndex: "getNodeByRangeIndex",
+ getComments: "getComments",
+ getCommentsBefore: "getCommentsBefore",
+ getCommentsAfter: "getCommentsAfter",
+ getCommentsInside: "getCommentsInside",
+ getJSDocComment: "getJSDocComment",
+ getFirstToken: "getFirstToken",
+ getFirstTokens: "getFirstTokens",
+ getLastToken: "getLastToken",
+ getLastTokens: "getLastTokens",
+ getTokenAfter: "getTokenAfter",
+ getTokenBefore: "getTokenBefore",
+ getTokenByRangeStart: "getTokenByRangeStart",
+ getTokens: "getTokens",
+ getTokensAfter: "getTokensAfter",
+ getTokensBefore: "getTokensBefore",
+ getTokensBetween: "getTokensBetween"
+};
+
+const BASE_TRAVERSAL_CONTEXT = Object.freeze(
+ Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
+ (contextInfo, methodName) =>
+ Object.assign(contextInfo, {
+ [methodName]() {
+ const sourceCode = this.getSourceCode();
+
+ return sourceCode[DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]].apply(sourceCode, arguments);
+ }
+ }),
+ {}
+ )
+);
+
+const lastSourceCodes = new WeakMap();
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Object that is responsible for verifying JavaScript text
+ * @name eslint
+ */
+module.exports = class Linter {
+
+ constructor() {
+ lastSourceCodes.set(this, null);
+ this.version = pkg.version;
+
+ this.rules = new Rules();
+ this._parsers = new Map();
+ this.environments = new Environments();
+ }
+
+ /**
+ * Configuration object for the `verify` API. A JS representation of the eslintrc files.
+ * @typedef {Object} ESLintConfig
+ * @property {Object} rules The rule configuration to verify against.
+ * @property {string} [parser] Parser to use when generatig the AST.
+ * @property {Object} [parserOptions] Options for the parsed used.
+ * @property {Object} [settings] Global settings passed to each rule.
+ * @property {Object} [env] The environment to verify in.
+ * @property {Object} [globals] Available globals to the code.
+ */
+
+ /**
+ * Same as linter.verify, except without support for processors.
+ * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
+ * @param {ESLintConfig} config An ESLintConfig instance to configure everything.
+ * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
+ * If this is not set, the filename will default to '<input>' in the rule context. If
+ * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
+ * @param {boolean} [filenameOrOptions.allowInlineConfig=true] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
+ * Useful if you want to validate JS without comments overriding rules.
+ * @param {boolean} [filenameOrOptions.reportUnusedDisableDirectives=false] Adds reported errors for unused
+ * eslint-disable directives
+ * @returns {Object[]} The results as an array of messages or null if no messages.
+ */
+ _verifyWithoutProcessors(textOrSourceCode, config, filenameOrOptions) {
+ let text,
+ parserServices,
+ allowInlineConfig,
+ providedFilename,
+ reportUnusedDisableDirectives;
+
+ // evaluate arguments
+ if (typeof filenameOrOptions === "object") {
+ providedFilename = filenameOrOptions.filename;
+ allowInlineConfig = filenameOrOptions.allowInlineConfig;
+ reportUnusedDisableDirectives = filenameOrOptions.reportUnusedDisableDirectives;
+ } else {
+ providedFilename = filenameOrOptions;
+ }
+
+ const filename = typeof providedFilename === "string" ? providedFilename : "<input>";
+
+ if (typeof textOrSourceCode === "string") {
+ lastSourceCodes.set(this, null);
+ text = textOrSourceCode;
+ } else {
+ lastSourceCodes.set(this, textOrSourceCode);
+ text = textOrSourceCode.text;
+ }
+
+ // search and apply "eslint-env *".
+ const envInFile = findEslintEnv(text);
+
+ config = Object.assign({}, config);
+
+ if (envInFile) {
+ if (config.env) {
+ config.env = Object.assign({}, config.env, envInFile);
+ } else {
+ config.env = envInFile;
+ }
+ }
+
+ // process initial config to make it safe to extend
+ config = prepareConfig(config, this.environments);
+
+ if (lastSourceCodes.get(this)) {
+ parserServices = {};
+ } else {
+
+ // there's no input, just exit here
+ if (text.trim().length === 0) {
+ lastSourceCodes.set(this, new SourceCode(text, blankScriptAST));
+ return [];
+ }
+
+ let parser;
+
+ try {
+ parser = this._parsers.get(config.parser) || require(config.parser);
+ } catch (ex) {
+ return [{
+ ruleId: null,
+ fatal: true,
+ severity: 2,
+ source: null,
+ message: ex.message,
+ line: 0,
+ column: 0
+ }];
+ }
+ const parseResult = parse(
+ stripUnicodeBOM(text).replace(astUtils.SHEBANG_MATCHER, (match, captured) => `//${captured}`),
+ config.parserOptions,
+ parser,
+ filename
+ );
+
+ if (!parseResult.success) {
+ return [parseResult.error];
+ }
+
+ parserServices = parseResult.services;
+ lastSourceCodes.set(this, new SourceCode(text, parseResult.ast));
+ }
+
+ const problems = [];
+ const sourceCode = lastSourceCodes.get(this);
+ let disableDirectives;
+
+ // parse global comments and modify config
+ if (allowInlineConfig !== false) {
+ const modifyConfigResult = modifyConfigsFromComments(filename, sourceCode.ast, config, ruleId => this.rules.get(ruleId));
+
+ config = modifyConfigResult.config;
+ modifyConfigResult.problems.forEach(problem => problems.push(problem));
+ disableDirectives = modifyConfigResult.disableDirectives;
+ } else {
+ disableDirectives = [];
+ }
+
+ const emitter = createEmitter();
+ const traverser = new Traverser();
+ const ecmaFeatures = config.parserOptions.ecmaFeatures || {};
+ const ecmaVersion = config.parserOptions.ecmaVersion || 5;
+ const scopeManager = eslintScope.analyze(sourceCode.ast, {
+ ignoreEval: true,
+ nodejsScope: ecmaFeatures.globalReturn,
+ impliedStrict: ecmaFeatures.impliedStrict,
+ ecmaVersion,
+ sourceType: config.parserOptions.sourceType || "script",
+ fallback: Traverser.getKeys
+ });
+
+ /*
+ * Create a frozen object with the ruleContext properties and methods that are shared by all rules.
+ * All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
+ * properties once for each rule.
+ */
+ const sharedTraversalContext = Object.freeze(
+ Object.assign(
+ Object.create(BASE_TRAVERSAL_CONTEXT),
+ {
+ getAncestors: () => traverser.parents(),
+ getDeclaredVariables: scopeManager.getDeclaredVariables.bind(scopeManager),
+ getFilename: () => filename,
+ getScope: () => getScope(scopeManager, traverser.current(), config.parserOptions.ecmaVersion),
+ getSourceCode: () => sourceCode,
+ markVariableAsUsed: name => markVariableAsUsed(scopeManager, traverser.current(), config.parserOptions, name),
+ parserOptions: config.parserOptions,
+ parserPath: config.parser,
+ parserServices,
+ settings: config.settings,
+
+ /**
+ * This is used to avoid breaking rules that used to monkeypatch the `Linter#report` method
+ * by using the `_linter` property on rule contexts.
+ *
+ * This should be removed in a major release after we create a better way to
+ * lint for unused disable comments.
+ * https://github.com/eslint/eslint/issues/9193
+ */
+ _linter: {
+ report() {},
+ on: emitter.on
+ }
+ }
+ )
+ );
+
+ // enable appropriate rules
+ Object.keys(config.rules).forEach(ruleId => {
+ const severity = ConfigOps.getRuleSeverity(config.rules[ruleId]);
+
+ if (severity === 0) {
+ return;
+ }
+
+ const rule = this.rules.get(ruleId);
+ let reportTranslator = null;
+ const ruleContext = Object.freeze(
+ Object.assign(
+ Object.create(sharedTraversalContext),
+ {
+ id: ruleId,
+ options: getRuleOptions(config.rules[ruleId]),
+ report() {
+
+ /*
+ * Create a report translator lazily.
+ * In a vast majority of cases, any given rule reports zero errors on a given
+ * piece of code. Creating a translator lazily avoids the performance cost of
+ * creating a new translator function for each rule that usually doesn't get
+ * called.
+ *
+ * Using lazy report translators improves end-to-end performance by about 3%
+ * with Node 8.4.0.
+ */
+ if (reportTranslator === null) {
+ reportTranslator = createReportTranslator({ ruleId, severity, sourceCode });
+ }
+ const problem = reportTranslator.apply(null, arguments);
+
+ if (problem.fix && rule.meta && !rule.meta.fixable) {
+ throw new Error("Fixable rules should export a `meta.fixable` property.");
+ }
+ problems.push(problem);
+
+ /*
+ * This is used to avoid breaking rules that used monkeypatch Linter, and relied on
+ * `linter.report` getting called with report info every time a rule reports a problem.
+ * To continue to support this, make sure that `context._linter.report` is called every
+ * time a problem is reported by a rule, even though `context._linter` is no longer a
+ * `Linter` instance.
+ *
+ * This should be removed in a major release after we create a better way to
+ * lint for unused disable comments.
+ * https://github.com/eslint/eslint/issues/9193
+ */
+ sharedTraversalContext._linter.report( // eslint-disable-line no-underscore-dangle
+ problem.ruleId,
+ problem.severity,
+ { loc: { start: { line: problem.line, column: problem.column - 1 } } },
+ problem.message
+ );
+ }
+ }
+ )
+ );
+
+ try {
+ const ruleListeners = rule.create(ruleContext);
+
+ // add all the selectors from the rule as listeners
+ Object.keys(ruleListeners).forEach(selector => {
+ emitter.on(
+ selector,
+ timing.enabled
+ ? timing.time(ruleId, ruleListeners[selector])
+ : ruleListeners[selector]
+ );
+ });
+ } catch (ex) {
+ ex.message = `Error while loading rule '${ruleId}': ${ex.message}`;
+ throw ex;
+ }
+ });
+
+ // augment global scope with declared global variables
+ addDeclaredGlobals(scopeManager.scopes[0], config, this.environments);
+
+ const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter));
+
+ /*
+ * Each node has a type property. Whenever a particular type of
+ * node is found, an event is fired. This allows any listeners to
+ * automatically be informed that this type of node has been found
+ * and react accordingly.
+ */
+ traverser.traverse(sourceCode.ast, {
+ enter(node, parent) {
+ node.parent = parent;
+ eventGenerator.enterNode(node);
+ },
+ leave(node) {
+ eventGenerator.leaveNode(node);
+ }
+ });
+
+ return applyDisableDirectives({
+ directives: disableDirectives,
+ problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
+ reportUnusedDisableDirectives
+ });
+ }
+
+ /**
+ * Verifies the text against the rules specified by the second argument.
+ * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
+ * @param {ESLintConfig} config An ESLintConfig instance to configure everything.
+ * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
+ * If this is not set, the filename will default to '<input>' in the rule context. If
+ * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
+ * @param {boolean} [saveState] Indicates if the state from the last run should be saved.
+ * Mostly useful for testing purposes.
+ * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
+ * Useful if you want to validate JS without comments overriding rules.
+ * @param {function(string): string[]} [filenameOrOptions.preprocess] preprocessor for source text. If provided,
+ * this should accept a string of source text, and return an array of code blocks to lint.
+ * @param {function(Array<Object[]>): Object[]} [filenameOrOptions.postprocess] postprocessor for report messages. If provided,
+ * this should accept an array of the message lists for each code block returned from the preprocessor,
+ * apply a mapping to the messages as appropriate, and return a one-dimensional array of messages
+ * @returns {Object[]} The results as an array of messages or null if no messages.
+ */
+ verify(textOrSourceCode, config, filenameOrOptions) {
+ const preprocess = filenameOrOptions && filenameOrOptions.preprocess || (rawText => [rawText]);
+ const postprocess = filenameOrOptions && filenameOrOptions.postprocess || lodash.flatten;
+
+ return postprocess(
+ preprocess(textOrSourceCode).map(
+ textBlock => this._verifyWithoutProcessors(textBlock, config, filenameOrOptions)
+ )
+ );
+ }
+
+ /**
+ * Gets the SourceCode object representing the parsed source.
+ * @returns {SourceCode} The SourceCode object.
+ */
+ getSourceCode() {
+ return lastSourceCodes.get(this);
+ }
+
+ /**
+ * Defines a new linting rule.
+ * @param {string} ruleId A unique rule identifier
+ * @param {Function} ruleModule Function from context to object mapping AST node types to event handlers
+ * @returns {void}
+ */
+ defineRule(ruleId, ruleModule) {
+ this.rules.define(ruleId, ruleModule);
+ }
+
+ /**
+ * Defines many new linting rules.
+ * @param {Object} rulesToDefine map from unique rule identifier to rule
+ * @returns {void}
+ */
+ defineRules(rulesToDefine) {
+ Object.getOwnPropertyNames(rulesToDefine).forEach(ruleId => {
+ this.defineRule(ruleId, rulesToDefine[ruleId]);
+ });
+ }
+
+ /**
+ * Gets an object with all loaded rules.
+ * @returns {Map} All loaded rules
+ */
+ getRules() {
+ return this.rules.getAllLoadedRules();
+ }
+
+ /**
+ * Define a new parser module
+ * @param {any} parserId Name of the parser
+ * @param {any} parserModule The parser object
+ * @returns {void}
+ */
+ defineParser(parserId, parserModule) {
+ this._parsers.set(parserId, parserModule);
+ }
+
+ /**
+ * Performs multiple autofix passes over the text until as many fixes as possible
+ * have been applied.
+ * @param {string} text The source text to apply fixes to.
+ * @param {Object} config The ESLint config object to use.
+ * @param {Object} options The ESLint options object to use.
+ * @param {string} options.filename The filename from which the text was read.
+ * @param {boolean} options.allowInlineConfig Flag indicating if inline comments
+ * should be allowed.
+ * @param {boolean|Function} options.fix Determines whether fixes should be applied
+ * @param {Function} options.preprocess preprocessor for source text. If provided, this should
+ * accept a string of source text, and return an array of code blocks to lint.
+ * @param {Function} options.postprocess postprocessor for report messages. If provided,
+ * this should accept an array of the message lists for each code block returned from the preprocessor,
+ * apply a mapping to the messages as appropriate, and return a one-dimensional array of messages
+ * @returns {Object} The result of the fix operation as returned from the
+ * SourceCodeFixer.
+ */
+ verifyAndFix(text, config, options) {
+ let messages = [],
+ fixedResult,
+ fixed = false,
+ passNumber = 0;
+ const debugTextDescription = options && options.filename || `${text.slice(0, 10)}...`;
+ const shouldFix = options && typeof options.fix !== "undefined" ? options.fix : true;
+
+ /**
+ * This loop continues until one of the following is true:
+ *
+ * 1. No more fixes have been applied.
+ * 2. Ten passes have been made.
+ *
+ * That means anytime a fix is successfully applied, there will be another pass.
+ * Essentially, guaranteeing a minimum of two passes.
+ */
+ do {
+ passNumber++;
+
+ debug(`Linting code for ${debugTextDescription} (pass ${passNumber})`);
+ messages = this.verify(text, config, options);
+
+ debug(`Generating fixed text for ${debugTextDescription} (pass ${passNumber})`);
+ fixedResult = SourceCodeFixer.applyFixes(text, messages, shouldFix);
+
+ /*
+ * stop if there are any syntax errors.
+ * 'fixedResult.output' is a empty string.
+ */
+ if (messages.length === 1 && messages[0].fatal) {
+ break;
+ }
+
+ // keep track if any fixes were ever applied - important for return value
+ fixed = fixed || fixedResult.fixed;
+
+ // update to use the fixed output instead of the original text
+ text = fixedResult.output;
+
+ } while (
+ fixedResult.fixed &&
+ passNumber < MAX_AUTOFIX_PASSES
+ );
+
+ /*
+ * If the last result had fixes, we need to lint again to be sure we have
+ * the most up-to-date information.
+ */
+ if (fixedResult.fixed) {
+ fixedResult.messages = this.verify(text, config, options);
+ }
+
+ // ensure the last result properly reflects if fixes were done
+ fixedResult.fixed = fixed;
+ fixedResult.output = text;
+
+ return fixedResult;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/load-rules.js b/tools/node_modules/eslint/lib/load-rules.js
new file mode 100644
index 0000000000..b74905d65a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/load-rules.js
@@ -0,0 +1,50 @@
+/**
+ * @fileoverview Module for loading rules from files and directories.
+ * @author Michael Ficarra
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path");
+
+const rulesDirCache = {};
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Load all rule modules from specified directory.
+ * @param {string} [rulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
+ * @param {string} cwd Current working directory
+ * @returns {Object} Loaded rule modules by rule ids (file names).
+ */
+module.exports = function(rulesDir, cwd) {
+ if (!rulesDir) {
+ rulesDir = path.join(__dirname, "rules");
+ } else {
+ rulesDir = path.resolve(cwd, rulesDir);
+ }
+
+ // cache will help performance as IO operation are expensive
+ if (rulesDirCache[rulesDir]) {
+ return rulesDirCache[rulesDir];
+ }
+
+ const rules = Object.create(null);
+
+ fs.readdirSync(rulesDir).forEach(file => {
+ if (path.extname(file) !== ".js") {
+ return;
+ }
+ rules[file.slice(0, -3)] = path.join(rulesDir, file);
+ });
+ rulesDirCache[rulesDir] = rules;
+
+ return rules;
+};
diff --git a/tools/node_modules/eslint/lib/logging.js b/tools/node_modules/eslint/lib/logging.js
new file mode 100644
index 0000000000..22451e535e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/logging.js
@@ -0,0 +1,28 @@
+/**
+ * @fileoverview Handle logging for ESLint
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+/* eslint no-console: "off" */
+
+/* istanbul ignore next */
+module.exports = {
+
+ /**
+ * Cover for console.log
+ * @returns {void}
+ */
+ info() {
+ console.log.apply(console, arguments);
+ },
+
+ /**
+ * Cover for console.error
+ * @returns {void}
+ */
+ error() {
+ console.error.apply(console, arguments);
+ }
+};
diff --git a/tools/node_modules/eslint/lib/options.js b/tools/node_modules/eslint/lib/options.js
new file mode 100644
index 0000000000..ee1d3369ce
--- /dev/null
+++ b/tools/node_modules/eslint/lib/options.js
@@ -0,0 +1,235 @@
+/**
+ * @fileoverview Options configuration for optionator.
+ * @author George Zahariev
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const optionator = require("optionator");
+
+//------------------------------------------------------------------------------
+// Initialization and Public Interface
+//------------------------------------------------------------------------------
+
+// exports "parse(args)", "generateHelp()", and "generateHelpForOption(optionName)"
+module.exports = optionator({
+ prepend: "eslint [options] file.js [file.js] [dir]",
+ defaults: {
+ concatRepeatedArrays: true,
+ mergeRepeatedObjects: true
+ },
+ options: [
+ {
+ heading: "Basic configuration"
+ },
+ {
+ option: "config",
+ alias: "c",
+ type: "path::String",
+ description: "Use configuration from this file or shareable config"
+ },
+ {
+ option: "eslintrc",
+ type: "Boolean",
+ default: "true",
+ description: "Disable use of configuration from .eslintrc"
+ },
+ {
+ option: "env",
+ type: "[String]",
+ description: "Specify environments"
+ },
+ {
+ option: "ext",
+ type: "[String]",
+ default: ".js",
+ description: "Specify JavaScript file extensions"
+ },
+ {
+ option: "global",
+ type: "[String]",
+ description: "Define global variables"
+ },
+ {
+ option: "parser",
+ type: "String",
+ description: "Specify the parser to be used"
+ },
+ {
+ option: "parser-options",
+ type: "Object",
+ description: "Specify parser options"
+ },
+ {
+ heading: "Caching"
+ },
+ {
+ option: "cache",
+ type: "Boolean",
+ default: "false",
+ description: "Only check changed files"
+ },
+ {
+ option: "cache-file",
+ type: "path::String",
+ default: ".eslintcache",
+ description: "Path to the cache file. Deprecated: use --cache-location"
+ },
+ {
+ option: "cache-location",
+ type: "path::String",
+ description: "Path to the cache file or directory"
+ },
+ {
+ heading: "Specifying rules and plugins"
+ },
+ {
+ option: "rulesdir",
+ type: "[path::String]",
+ description: "Use additional rules from this directory"
+ },
+ {
+ option: "plugin",
+ type: "[String]",
+ description: "Specify plugins"
+ },
+ {
+ option: "rule",
+ type: "Object",
+ description: "Specify rules"
+ },
+ {
+ heading: "Ignoring files"
+ },
+ {
+ option: "ignore-path",
+ type: "path::String",
+ description: "Specify path of ignore file"
+ },
+ {
+ option: "ignore",
+ type: "Boolean",
+ default: "true",
+ description: "Disable use of ignore files and patterns"
+ },
+ {
+ option: "ignore-pattern",
+ type: "[String]",
+ description: "Pattern of files to ignore (in addition to those in .eslintignore)",
+ concatRepeatedArrays: [true, {
+ oneValuePerFlag: true
+ }]
+ },
+ {
+ heading: "Using stdin"
+ },
+ {
+ option: "stdin",
+ type: "Boolean",
+ default: "false",
+ description: "Lint code provided on <STDIN>"
+ },
+ {
+ option: "stdin-filename",
+ type: "String",
+ description: "Specify filename to process STDIN as"
+ },
+ {
+ heading: "Handling warnings"
+ },
+ {
+ option: "quiet",
+ type: "Boolean",
+ default: "false",
+ description: "Report errors only"
+ },
+ {
+ option: "max-warnings",
+ type: "Int",
+ default: "-1",
+ description: "Number of warnings to trigger nonzero exit code"
+ },
+ {
+ heading: "Output"
+ },
+ {
+ option: "output-file",
+ alias: "o",
+ type: "path::String",
+ description: "Specify file to write report to"
+ },
+ {
+ option: "format",
+ alias: "f",
+ type: "String",
+ default: "stylish",
+ description: "Use a specific output format"
+ },
+ {
+ option: "color",
+ type: "Boolean",
+ alias: "no-color",
+ description: "Force enabling/disabling of color"
+ },
+ {
+ heading: "Miscellaneous"
+ },
+ {
+ option: "init",
+ type: "Boolean",
+ default: "false",
+ description: "Run config initialization wizard"
+ },
+ {
+ option: "fix",
+ type: "Boolean",
+ default: false,
+ description: "Automatically fix problems"
+ },
+ {
+ option: "fix-dry-run",
+ type: "Boolean",
+ default: false,
+ description: "Automatically fix problems without saving the changes to the file system"
+ },
+ {
+ option: "debug",
+ type: "Boolean",
+ default: false,
+ description: "Output debugging information"
+ },
+ {
+ option: "help",
+ alias: "h",
+ type: "Boolean",
+ description: "Show help"
+ },
+ {
+ option: "version",
+ alias: "v",
+ type: "Boolean",
+ description: "Output the version number"
+ },
+ {
+ option: "inline-config",
+ type: "Boolean",
+ default: "true",
+ description: "Prevent comments from changing config or rules"
+ },
+ {
+ option: "report-unused-disable-directives",
+ type: "Boolean",
+ default: false,
+ description: "Adds reported errors for unused eslint-disable directives"
+ },
+ {
+ option: "print-config",
+ type: "path::String",
+ description: "Print the configuration for the given file"
+ }
+ ]
+});
diff --git a/tools/node_modules/eslint/lib/report-translator.js b/tools/node_modules/eslint/lib/report-translator.js
new file mode 100644
index 0000000000..9c4ab8b9a9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/report-translator.js
@@ -0,0 +1,274 @@
+/**
+ * @fileoverview A helper that translates context.report() calls from the rule API into generic problem objects
+ * @author Teddy Katz
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("assert");
+const ruleFixer = require("./util/rule-fixer");
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * An error message description
+ * @typedef {Object} MessageDescriptor
+ * @property {ASTNode} [node] The reported node
+ * @property {Location} loc The location of the problem.
+ * @property {string} message The problem message.
+ * @property {Object} [data] Optional data to use to fill in placeholders in the
+ * message.
+ * @property {Function} [fix] The function to call that creates a fix command.
+ */
+
+//------------------------------------------------------------------------------
+// Module Definition
+//------------------------------------------------------------------------------
+
+
+/**
+ * Translates a multi-argument context.report() call into a single object argument call
+ * @param {...*} arguments A list of arguments passed to `context.report`
+ * @returns {MessageDescriptor} A normalized object containing report information
+ */
+function normalizeMultiArgReportCall() {
+
+ // If there is one argument, it is considered to be a new-style call already.
+ if (arguments.length === 1) {
+ return arguments[0];
+ }
+
+ // If the second argument is a string, the arguments are interpreted as [node, message, data, fix].
+ if (typeof arguments[1] === "string") {
+ return {
+ node: arguments[0],
+ message: arguments[1],
+ data: arguments[2],
+ fix: arguments[3]
+ };
+ }
+
+ // Otherwise, the arguments are interpreted as [node, loc, message, data, fix].
+ return {
+ node: arguments[0],
+ loc: arguments[1],
+ message: arguments[2],
+ data: arguments[3],
+ fix: arguments[4]
+ };
+}
+
+/**
+ * Asserts that either a loc or a node was provided, and the node is valid if it was provided.
+ * @param {MessageDescriptor} descriptor A descriptor to validate
+ * @returns {void}
+ * @throws AssertionError if neither a node nor a loc was provided, or if the node is not an object
+ */
+function assertValidNodeInfo(descriptor) {
+ if (descriptor.node) {
+ assert(typeof descriptor.node === "object", "Node must be an object");
+ } else {
+ assert(descriptor.loc, "Node must be provided when reporting error if location is not provided");
+ }
+}
+
+/**
+ * Normalizes a MessageDescriptor to always have a `loc` with `start` and `end` properties
+ * @param {MessageDescriptor} descriptor A descriptor for the report from a rule.
+ * @returns {{start: Location, end: (Location|null)}} An updated location that infers the `start` and `end` properties
+ * from the `node` of the original descriptor, or infers the `start` from the `loc` of the original descriptor.
+ */
+function normalizeReportLoc(descriptor) {
+ if (descriptor.loc) {
+ if (descriptor.loc.start) {
+ return descriptor.loc;
+ }
+ return { start: descriptor.loc, end: null };
+ }
+ return descriptor.node.loc;
+}
+
+/**
+ * Interpolates data placeholders in report messages
+ * @param {MessageDescriptor} descriptor The report message descriptor.
+ * @returns {string} The interpolated message for the descriptor
+ */
+function normalizeMessagePlaceholders(descriptor) {
+ if (!descriptor.data) {
+ return descriptor.message;
+ }
+ return descriptor.message.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (fullMatch, term) => {
+ if (term in descriptor.data) {
+ return descriptor.data[term];
+ }
+
+ return fullMatch;
+ });
+}
+
+/**
+ * Compares items in a fixes array by range.
+ * @param {Fix} a The first message.
+ * @param {Fix} b The second message.
+ * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
+ * @private
+ */
+function compareFixesByRange(a, b) {
+ return a.range[0] - b.range[0] || a.range[1] - b.range[1];
+}
+
+/**
+ * Merges the given fixes array into one.
+ * @param {Fix[]} fixes The fixes to merge.
+ * @param {SourceCode} sourceCode The source code object to get the text between fixes.
+ * @returns {{text: string, range: [number, number]}} The merged fixes
+ */
+function mergeFixes(fixes, sourceCode) {
+ if (fixes.length === 0) {
+ return null;
+ }
+ if (fixes.length === 1) {
+ return fixes[0];
+ }
+
+ fixes.sort(compareFixesByRange);
+
+ const originalText = sourceCode.text;
+ const start = fixes[0].range[0];
+ const end = fixes[fixes.length - 1].range[1];
+ let text = "";
+ let lastPos = Number.MIN_SAFE_INTEGER;
+
+ for (const fix of fixes) {
+ assert(fix.range[0] >= lastPos, "Fix objects must not be overlapped in a report.");
+
+ if (fix.range[0] >= 0) {
+ text += originalText.slice(Math.max(0, start, lastPos), fix.range[0]);
+ }
+ text += fix.text;
+ lastPos = fix.range[1];
+ }
+ text += originalText.slice(Math.max(0, start, lastPos), end);
+
+ return { range: [start, end], text };
+}
+
+/**
+ * Gets one fix object from the given descriptor.
+ * If the descriptor retrieves multiple fixes, this merges those to one.
+ * @param {MessageDescriptor} descriptor The report descriptor.
+ * @param {SourceCode} sourceCode The source code object to get text between fixes.
+ * @returns {({text: string, range: [number, number]}|null)} The fix for the descriptor
+ */
+function normalizeFixes(descriptor, sourceCode) {
+ if (typeof descriptor.fix !== "function") {
+ return null;
+ }
+
+ // @type {null | Fix | Fix[] | IterableIterator<Fix>}
+ const fix = descriptor.fix(ruleFixer);
+
+ // Merge to one.
+ if (fix && Symbol.iterator in fix) {
+ return mergeFixes(Array.from(fix), sourceCode);
+ }
+ return fix;
+}
+
+/**
+ * Creates information about the report from a descriptor
+ * @param {{
+ * ruleId: string,
+ * severity: (0|1|2),
+ * node: (ASTNode|null),
+ * message: string,
+ * loc: {start: SourceLocation, end: (SourceLocation|null)},
+ * fix: ({text: string, range: [number, number]}|null),
+ * sourceLines: string[]
+ * }} options Information about the problem
+ * @returns {function(...args): {
+ * ruleId: string,
+ * severity: (0|1|2),
+ * message: string,
+ * line: number,
+ * column: number,
+ * endLine: (number|undefined),
+ * endColumn: (number|undefined),
+ * nodeType: (string|null),
+ * source: string,
+ * fix: ({text: string, range: [number, number]}|null)
+ * }} Information about the report
+ */
+function createProblem(options) {
+ const problem = {
+ ruleId: options.ruleId,
+ severity: options.severity,
+ message: options.message,
+ line: options.loc.start.line,
+ column: options.loc.start.column + 1,
+ nodeType: options.node && options.node.type || null,
+ source: options.sourceLines[options.loc.start.line - 1] || ""
+ };
+
+ if (options.loc.end) {
+ problem.endLine = options.loc.end.line;
+ problem.endColumn = options.loc.end.column + 1;
+ }
+
+ if (options.fix) {
+ problem.fix = options.fix;
+ }
+
+ return problem;
+}
+
+/**
+ * Returns a function that converts the arguments of a `context.report` call from a rule into a reported
+ * problem for the Node.js API.
+ * @param {{ruleId: string, severity: number, sourceCode: SourceCode}} metadata Metadata for the reported problem
+ * @param {SourceCode} sourceCode The `SourceCode` instance for the text being linted
+ * @returns {function(...args): {
+ * ruleId: string,
+ * severity: (0|1|2),
+ * message: string,
+ * line: number,
+ * column: number,
+ * endLine: (number|undefined),
+ * endColumn: (number|undefined),
+ * nodeType: (string|null),
+ * source: string,
+ * fix: ({text: string, range: [number, number]}|null)
+ * }}
+ * Information about the report
+ */
+
+module.exports = function createReportTranslator(metadata) {
+
+ /*
+ * `createReportTranslator` gets called once per enabled rule per file. It needs to be very performant.
+ * The report translator itself (i.e. the function that `createReportTranslator` returns) gets
+ * called every time a rule reports a problem, which happens much less frequently (usually, the vast
+ * majority of rules don't report any problems for a given file).
+ */
+ return function() {
+ const descriptor = normalizeMultiArgReportCall.apply(null, arguments);
+
+ assertValidNodeInfo(descriptor);
+
+ return createProblem({
+ ruleId: metadata.ruleId,
+ severity: metadata.severity,
+ node: descriptor.node,
+ message: normalizeMessagePlaceholders(descriptor),
+ loc: normalizeReportLoc(descriptor),
+ fix: normalizeFixes(descriptor, metadata.sourceCode),
+ sourceLines: metadata.sourceCode.lines
+ });
+ };
+};
diff --git a/tools/node_modules/eslint/lib/rules.js b/tools/node_modules/eslint/lib/rules.js
new file mode 100644
index 0000000000..040f9db505
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules.js
@@ -0,0 +1,140 @@
+/**
+ * @fileoverview Defines a storage for rules.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+const loadRules = require("./load-rules");
+const ruleReplacements = require("../conf/replacements").rules;
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Creates a stub rule that gets used when a rule with a given ID is not found.
+ * @param {string} ruleId The ID of the missing rule
+ * @returns {{create: function(RuleContext): Object}} A rule that reports an error at the first location
+ * in the program. The report has the message `Definition for rule '${ruleId}' was not found` if the rule is unknown,
+ * or `Rule '${ruleId}' was removed and replaced by: ${replacements.join(", ")}` if the rule is known to have been
+ * replaced.
+ */
+const createMissingRule = lodash.memoize(ruleId => {
+ const message = Object.prototype.hasOwnProperty.call(ruleReplacements, ruleId)
+ ? `Rule '${ruleId}' was removed and replaced by: ${ruleReplacements[ruleId].join(", ")}`
+ : `Definition for rule '${ruleId}' was not found`;
+
+ return {
+ create: context => ({
+ Program() {
+ context.report({
+ loc: { line: 1, column: 0 },
+ message
+ });
+ }
+ })
+ };
+});
+
+/**
+ * Normalizes a rule module to the new-style API
+ * @param {(Function|{create: Function})} rule A rule object, which can either be a function
+ * ("old-style") or an object with a `create` method ("new-style")
+ * @returns {{create: Function}} A new-style rule.
+ */
+function normalizeRule(rule) {
+ return typeof rule === "function" ? Object.assign({ create: rule }, rule) : rule;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+class Rules {
+ constructor() {
+ this._rules = Object.create(null);
+
+ this.load();
+ }
+
+ /**
+ * Registers a rule module for rule id in storage.
+ * @param {string} ruleId Rule id (file name).
+ * @param {Function} ruleModule Rule handler.
+ * @returns {void}
+ */
+ define(ruleId, ruleModule) {
+ this._rules[ruleId] = normalizeRule(ruleModule);
+ }
+
+ /**
+ * Loads and registers all rules from passed rules directory.
+ * @param {string} [rulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
+ * @param {string} cwd Current working directory
+ * @returns {void}
+ */
+ load(rulesDir, cwd) {
+ const newRules = loadRules(rulesDir, cwd);
+
+ Object.keys(newRules).forEach(ruleId => {
+ this.define(ruleId, newRules[ruleId]);
+ });
+ }
+
+ /**
+ * Registers all given rules of a plugin.
+ * @param {Object} plugin The plugin object to import.
+ * @param {string} pluginName The name of the plugin without prefix (`eslint-plugin-`).
+ * @returns {void}
+ */
+ importPlugin(plugin, pluginName) {
+ if (plugin.rules) {
+ Object.keys(plugin.rules).forEach(ruleId => {
+ const qualifiedRuleId = `${pluginName}/${ruleId}`,
+ rule = plugin.rules[ruleId];
+
+ this.define(qualifiedRuleId, rule);
+ });
+ }
+ }
+
+ /**
+ * Access rule handler by id (file name).
+ * @param {string} ruleId Rule id (file name).
+ * @returns {{create: Function, schema: JsonSchema[]}}
+ * A rule. This is normalized to always have the new-style shape with a `create` method.
+ */
+ get(ruleId) {
+ if (!Object.prototype.hasOwnProperty.call(this._rules, ruleId)) {
+ return createMissingRule(ruleId);
+ }
+ if (typeof this._rules[ruleId] === "string") {
+ return normalizeRule(require(this._rules[ruleId]));
+ }
+ return this._rules[ruleId];
+
+ }
+
+ /**
+ * Get an object with all currently loaded rules
+ * @returns {Map} All loaded rules
+ */
+ getAllLoadedRules() {
+ const allRules = new Map();
+
+ Object.keys(this._rules).forEach(name => {
+ const rule = this.get(name);
+
+ allRules.set(name, rule);
+ });
+ return allRules;
+ }
+}
+
+module.exports = Rules;
diff --git a/tools/node_modules/eslint/lib/rules/.eslintrc.yml b/tools/node_modules/eslint/lib/rules/.eslintrc.yml
new file mode 100644
index 0000000000..2a8d907935
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/.eslintrc.yml
@@ -0,0 +1,3 @@
+rules:
+ rulesdir/no-invalid-meta: "error"
+ rulesdir/consistent-docs-description: "error"
diff --git a/tools/node_modules/eslint/lib/rules/accessor-pairs.js b/tools/node_modules/eslint/lib/rules/accessor-pairs.js
new file mode 100644
index 0000000000..4afdc7136c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/accessor-pairs.js
@@ -0,0 +1,156 @@
+/**
+ * @fileoverview Rule to flag wrapping non-iife in parens
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is an `Identifier` node which was named a given name.
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - An expected name of the node.
+ * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
+ */
+function isIdentifier(node, name) {
+ return node.type === "Identifier" && node.name === name;
+}
+
+/**
+ * Checks whether or not a given node is an argument of a specified method call.
+ * @param {ASTNode} node - A node to check.
+ * @param {number} index - An expected index of the node in arguments.
+ * @param {string} object - An expected name of the object of the method.
+ * @param {string} property - An expected name of the method.
+ * @returns {boolean} `true` if the node is an argument of the specified method call.
+ */
+function isArgumentOfMethodCall(node, index, object, property) {
+ const parent = node.parent;
+
+ return (
+ parent.type === "CallExpression" &&
+ parent.callee.type === "MemberExpression" &&
+ parent.callee.computed === false &&
+ isIdentifier(parent.callee.object, object) &&
+ isIdentifier(parent.callee.property, property) &&
+ parent.arguments[index] === node
+ );
+}
+
+/**
+ * Checks whether or not a given node is a property descriptor.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a property descriptor.
+ */
+function isPropertyDescriptor(node) {
+
+ // Object.defineProperty(obj, "foo", {set: ...})
+ if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") ||
+ isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty")
+ ) {
+ return true;
+ }
+
+ /*
+ * Object.defineProperties(obj, {foo: {set: ...}})
+ * Object.create(proto, {foo: {set: ...}})
+ */
+ node = node.parent.parent;
+
+ return node.type === "ObjectExpression" && (
+ isArgumentOfMethodCall(node, 1, "Object", "create") ||
+ isArgumentOfMethodCall(node, 1, "Object", "defineProperties")
+ );
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce getter and setter pairs in objects",
+ category: "Best Practices",
+ recommended: false
+ },
+ schema: [{
+ type: "object",
+ properties: {
+ getWithoutSet: {
+ type: "boolean"
+ },
+ setWithoutGet: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+ create(context) {
+ const config = context.options[0] || {};
+ const checkGetWithoutSet = config.getWithoutSet === true;
+ const checkSetWithoutGet = config.setWithoutGet !== false;
+
+ /**
+ * Checks a object expression to see if it has setter and getter both present or none.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkLonelySetGet(node) {
+ let isSetPresent = false;
+ let isGetPresent = false;
+ const isDescriptor = isPropertyDescriptor(node);
+
+ for (let i = 0, end = node.properties.length; i < end; i++) {
+ const property = node.properties[i];
+
+ let propToCheck = "";
+
+ if (property.kind === "init") {
+ if (isDescriptor && !property.computed) {
+ propToCheck = property.key.name;
+ }
+ } else {
+ propToCheck = property.kind;
+ }
+
+ switch (propToCheck) {
+ case "set":
+ isSetPresent = true;
+ break;
+
+ case "get":
+ isGetPresent = true;
+ break;
+
+ default:
+
+ // Do nothing
+ }
+
+ if (isSetPresent && isGetPresent) {
+ break;
+ }
+ }
+
+ if (checkSetWithoutGet && isSetPresent && !isGetPresent) {
+ context.report({ node, message: "Getter is not present." });
+ } else if (checkGetWithoutSet && isGetPresent && !isSetPresent) {
+ context.report({ node, message: "Setter is not present." });
+ }
+ }
+
+ return {
+ ObjectExpression(node) {
+ if (checkSetWithoutGet || checkGetWithoutSet) {
+ checkLonelySetGet(node);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/array-bracket-newline.js b/tools/node_modules/eslint/lib/rules/array-bracket-newline.js
new file mode 100644
index 0000000000..cb7350a825
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/array-bracket-newline.js
@@ -0,0 +1,249 @@
+/**
+ * @fileoverview Rule to enforce linebreaks after open and before close array brackets
+ * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce linebreaks after opening and before closing array brackets",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never", "consistent"]
+ },
+ {
+ type: "object",
+ properties: {
+ multiline: {
+ type: "boolean"
+ },
+ minItems: {
+ type: ["integer", "null"],
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Normalizes a given option value.
+ *
+ * @param {string|Object|undefined} option - An option value to parse.
+ * @returns {{multiline: boolean, minItems: number}} Normalized option object.
+ */
+ function normalizeOptionValue(option) {
+ let consistent = false;
+ let multiline = false;
+ let minItems = 0;
+
+ if (option) {
+ if (option === "consistent") {
+ consistent = true;
+ minItems = Number.POSITIVE_INFINITY;
+ } else if (option === "always" || option.minItems === 0) {
+ minItems = 0;
+ } else if (option === "never") {
+ minItems = Number.POSITIVE_INFINITY;
+ } else {
+ multiline = Boolean(option.multiline);
+ minItems = option.minItems || Number.POSITIVE_INFINITY;
+ }
+ } else {
+ consistent = false;
+ multiline = true;
+ minItems = Number.POSITIVE_INFINITY;
+ }
+
+ return { consistent, multiline, minItems };
+ }
+
+ /**
+ * Normalizes a given option value.
+ *
+ * @param {string|Object|undefined} options - An option value to parse.
+ * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
+ */
+ function normalizeOptions(options) {
+ const value = normalizeOptionValue(options);
+
+ return { ArrayExpression: value, ArrayPattern: value };
+ }
+
+ /**
+ * Reports that there shouldn't be a linebreak after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoBeginningLinebreak(node, token) {
+ context.report({
+ node,
+ loc: token.loc,
+ message: "There should be no linebreak after '['.",
+ fix(fixer) {
+ const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
+
+ if (astUtils.isCommentToken(nextToken)) {
+ return null;
+ }
+
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a linebreak before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoEndingLinebreak(node, token) {
+ context.report({
+ node,
+ loc: token.loc,
+ message: "There should be no linebreak before ']'.",
+ fix(fixer) {
+ const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
+
+ if (astUtils.isCommentToken(previousToken)) {
+ return null;
+ }
+
+ return fixer.removeRange([previousToken.range[1], token.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a linebreak after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredBeginningLinebreak(node, token) {
+ context.report({
+ node,
+ loc: token.loc,
+ message: "A linebreak is required after '['.",
+ fix(fixer) {
+ return fixer.insertTextAfter(token, "\n");
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a linebreak before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredEndingLinebreak(node, token) {
+ context.report({
+ node,
+ loc: token.loc,
+ message: "A linebreak is required before ']'.",
+ fix(fixer) {
+ return fixer.insertTextBefore(token, "\n");
+ }
+ });
+ }
+
+ /**
+ * Reports a given node if it violated this rule.
+ *
+ * @param {ASTNode} node - A node to check. This is an ArrayExpression node or an ArrayPattern node.
+ * @returns {void}
+ */
+ function check(node) {
+ const elements = node.elements;
+ const normalizedOptions = normalizeOptions(context.options[0]);
+ const options = normalizedOptions[node.type];
+ const openBracket = sourceCode.getFirstToken(node);
+ const closeBracket = sourceCode.getLastToken(node);
+ const firstIncComment = sourceCode.getTokenAfter(openBracket, { includeComments: true });
+ const lastIncComment = sourceCode.getTokenBefore(closeBracket, { includeComments: true });
+ const first = sourceCode.getTokenAfter(openBracket);
+ const last = sourceCode.getTokenBefore(closeBracket);
+
+ const needsLinebreaks = (
+ elements.length >= options.minItems ||
+ (
+ options.multiline &&
+ elements.length > 0 &&
+ firstIncComment.loc.start.line !== lastIncComment.loc.end.line
+ ) ||
+ (
+ elements.length === 0 &&
+ firstIncComment.type === "Block" &&
+ firstIncComment.loc.start.line !== lastIncComment.loc.end.line &&
+ firstIncComment === lastIncComment
+ ) ||
+ (
+ options.consistent &&
+ firstIncComment.loc.start.line !== openBracket.loc.end.line
+ )
+ );
+
+ /*
+ * Use tokens or comments to check multiline or not.
+ * But use only tokens to check whether linebreaks are needed.
+ * This allows:
+ * var arr = [ // eslint-disable-line foo
+ * 'a'
+ * ]
+ */
+
+ if (needsLinebreaks) {
+ if (astUtils.isTokenOnSameLine(openBracket, first)) {
+ reportRequiredBeginningLinebreak(node, openBracket);
+ }
+ if (astUtils.isTokenOnSameLine(last, closeBracket)) {
+ reportRequiredEndingLinebreak(node, closeBracket);
+ }
+ } else {
+ if (!astUtils.isTokenOnSameLine(openBracket, first)) {
+ reportNoBeginningLinebreak(node, openBracket);
+ }
+ if (!astUtils.isTokenOnSameLine(last, closeBracket)) {
+ reportNoEndingLinebreak(node, closeBracket);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ ArrayPattern: check,
+ ArrayExpression: check
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/array-bracket-spacing.js b/tools/node_modules/eslint/lib/rules/array-bracket-spacing.js
new file mode 100644
index 0000000000..aecef2c4f2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/array-bracket-spacing.js
@@ -0,0 +1,229 @@
+/**
+ * @fileoverview Disallows or enforces spaces inside of array brackets.
+ * @author Jamund Ferguson
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing inside array brackets",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ singleValue: {
+ type: "boolean"
+ },
+ objectsInArrays: {
+ type: "boolean"
+ },
+ arraysInArrays: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ create(context) {
+ const spaced = context.options[0] === "always",
+ sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether an option is set, relative to the spacing option.
+ * If spaced is "always", then check whether option is set to false.
+ * If spaced is "never", then check whether option is set to true.
+ * @param {Object} option - The option to exclude.
+ * @returns {boolean} Whether or not the property is excluded.
+ */
+ function isOptionSet(option) {
+ return context.options[1] ? context.options[1][option] === !spaced : false;
+ }
+
+ const options = {
+ spaced,
+ singleElementException: isOptionSet("singleValue"),
+ objectsInArraysException: isOptionSet("objectsInArrays"),
+ arraysInArraysException: isOptionSet("arraysInArrays")
+ };
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports that there shouldn't be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoBeginningSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "There should be no space after '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ const nextToken = sourceCode.getTokenAfter(token);
+
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoEndingSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "There should be no space before '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ const previousToken = sourceCode.getTokenBefore(token);
+
+ return fixer.removeRange([previousToken.range[1], token.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredBeginningSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "A space is required after '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredEndingSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "A space is required before '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ });
+ }
+
+ /**
+ * Determines if a node is an object type
+ * @param {ASTNode} node - The node to check.
+ * @returns {boolean} Whether or not the node is an object type.
+ */
+ function isObjectType(node) {
+ return node && (node.type === "ObjectExpression" || node.type === "ObjectPattern");
+ }
+
+ /**
+ * Determines if a node is an array type
+ * @param {ASTNode} node - The node to check.
+ * @returns {boolean} Whether or not the node is an array type.
+ */
+ function isArrayType(node) {
+ return node && (node.type === "ArrayExpression" || node.type === "ArrayPattern");
+ }
+
+ /**
+ * Validates the spacing around array brackets
+ * @param {ASTNode} node - The node we're checking for spacing
+ * @returns {void}
+ */
+ function validateArraySpacing(node) {
+ if (options.spaced && node.elements.length === 0) {
+ return;
+ }
+
+ const first = sourceCode.getFirstToken(node),
+ second = sourceCode.getFirstToken(node, 1),
+ last = node.typeAnnotation
+ ? sourceCode.getTokenBefore(node.typeAnnotation)
+ : sourceCode.getLastToken(node),
+ penultimate = sourceCode.getTokenBefore(last),
+ firstElement = node.elements[0],
+ lastElement = node.elements[node.elements.length - 1];
+
+ const openingBracketMustBeSpaced =
+ options.objectsInArraysException && isObjectType(firstElement) ||
+ options.arraysInArraysException && isArrayType(firstElement) ||
+ options.singleElementException && node.elements.length === 1
+ ? !options.spaced : options.spaced;
+
+ const closingBracketMustBeSpaced =
+ options.objectsInArraysException && isObjectType(lastElement) ||
+ options.arraysInArraysException && isArrayType(lastElement) ||
+ options.singleElementException && node.elements.length === 1
+ ? !options.spaced : options.spaced;
+
+ if (astUtils.isTokenOnSameLine(first, second)) {
+ if (openingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(first, second)) {
+ reportRequiredBeginningSpace(node, first);
+ }
+ if (!openingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(first, second)) {
+ reportNoBeginningSpace(node, first);
+ }
+ }
+
+ if (first !== penultimate && astUtils.isTokenOnSameLine(penultimate, last)) {
+ if (closingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(penultimate, last)) {
+ reportRequiredEndingSpace(node, last);
+ }
+ if (!closingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(penultimate, last)) {
+ reportNoEndingSpace(node, last);
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ ArrayPattern: validateArraySpacing,
+ ArrayExpression: validateArraySpacing
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/array-callback-return.js b/tools/node_modules/eslint/lib/rules/array-callback-return.js
new file mode 100644
index 0000000000..37d6ebe3a7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/array-callback-return.js
@@ -0,0 +1,232 @@
+/**
+ * @fileoverview Rule to enforce return statements in callbacks of array's methods
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/;
+const TARGET_METHODS = /^(?:every|filter|find(?:Index)?|map|reduce(?:Right)?|some|sort)$/;
+
+/**
+ * Checks a given code path segment is reachable.
+ *
+ * @param {CodePathSegment} segment - A segment to check.
+ * @returns {boolean} `true` if the segment is reachable.
+ */
+function isReachable(segment) {
+ return segment.reachable;
+}
+
+/**
+ * Gets a readable location.
+ *
+ * - FunctionExpression -> the function name or `function` keyword.
+ * - ArrowFunctionExpression -> `=>` token.
+ *
+ * @param {ASTNode} node - A function node to get.
+ * @param {SourceCode} sourceCode - A source code to get tokens.
+ * @returns {ASTNode|Token} The node or the token of a location.
+ */
+function getLocation(node, sourceCode) {
+ if (node.type === "ArrowFunctionExpression") {
+ return sourceCode.getTokenBefore(node.body);
+ }
+ return node.id || node;
+}
+
+/**
+ * Checks a given node is a MemberExpression node which has the specified name's
+ * property.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a MemberExpression node which has
+ * the specified name's property
+ */
+function isTargetMethod(node) {
+ return (
+ node.type === "MemberExpression" &&
+ TARGET_METHODS.test(astUtils.getStaticPropertyName(node) || "")
+ );
+}
+
+/**
+ * Checks whether or not a given node is a function expression which is the
+ * callback of an array method.
+ *
+ * @param {ASTNode} node - A node to check. This is one of
+ * FunctionExpression or ArrowFunctionExpression.
+ * @returns {boolean} `true` if the node is the callback of an array method.
+ */
+function isCallbackOfArrayMethod(node) {
+ while (node) {
+ const parent = node.parent;
+
+ switch (parent.type) {
+
+ /*
+ * Looks up the destination. e.g.,
+ * foo.every(nativeFoo || function foo() { ... });
+ */
+ case "LogicalExpression":
+ case "ConditionalExpression":
+ node = parent;
+ break;
+
+ /*
+ * If the upper function is IIFE, checks the destination of the return value.
+ * e.g.
+ * foo.every((function() {
+ * // setup...
+ * return function callback() { ... };
+ * })());
+ */
+ case "ReturnStatement": {
+ const func = astUtils.getUpperFunction(parent);
+
+ if (func === null || !astUtils.isCallee(func)) {
+ return false;
+ }
+ node = func.parent;
+ break;
+ }
+
+ /*
+ * e.g.
+ * Array.from([], function() {});
+ * list.every(function() {});
+ */
+ case "CallExpression":
+ if (astUtils.isArrayFromMethod(parent.callee)) {
+ return (
+ parent.arguments.length >= 2 &&
+ parent.arguments[1] === node
+ );
+ }
+ if (isTargetMethod(parent.callee)) {
+ return (
+ parent.arguments.length >= 1 &&
+ parent.arguments[0] === node
+ );
+ }
+ return false;
+
+ // Otherwise this node is not target.
+ default:
+ return false;
+ }
+ }
+
+ /* istanbul ignore next: unreachable */
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce `return` statements in callbacks of array methods",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ let funcInfo = {
+ upper: null,
+ codePath: null,
+ hasReturn: false,
+ shouldCheck: false,
+ node: null
+ };
+
+ /**
+ * Checks whether or not the last code path segment is reachable.
+ * Then reports this function if the segment is reachable.
+ *
+ * If the last code path segment is reachable, there are paths which are not
+ * returned or thrown.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {void}
+ */
+ function checkLastSegment(node) {
+ if (funcInfo.shouldCheck &&
+ funcInfo.codePath.currentSegments.some(isReachable)
+ ) {
+ context.report({
+ node,
+ loc: getLocation(node, context.getSourceCode()).loc.start,
+ message: funcInfo.hasReturn
+ ? "Expected to return a value at the end of {{name}}."
+ : "Expected to return a value in {{name}}.",
+ data: {
+ name: astUtils.getFunctionNameWithKind(funcInfo.node)
+ }
+ });
+ }
+ }
+
+ return {
+
+ // Stacks this function's information.
+ onCodePathStart(codePath, node) {
+ funcInfo = {
+ upper: funcInfo,
+ codePath,
+ hasReturn: false,
+ shouldCheck:
+ TARGET_NODE_TYPE.test(node.type) &&
+ node.body.type === "BlockStatement" &&
+ isCallbackOfArrayMethod(node) &&
+ !node.async &&
+ !node.generator,
+ node
+ };
+ },
+
+ // Pops this function's information.
+ onCodePathEnd() {
+ funcInfo = funcInfo.upper;
+ },
+
+ // Checks the return statement is valid.
+ ReturnStatement(node) {
+ if (funcInfo.shouldCheck) {
+ funcInfo.hasReturn = true;
+
+ if (!node.argument) {
+ context.report({
+ node,
+ message: "{{name}} expected a return value.",
+ data: {
+ name: lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
+ }
+ });
+ }
+ }
+ },
+
+ // Reports a given function if the last path is reachable.
+ "FunctionExpression:exit": checkLastSegment,
+ "ArrowFunctionExpression:exit": checkLastSegment
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/array-element-newline.js b/tools/node_modules/eslint/lib/rules/array-element-newline.js
new file mode 100644
index 0000000000..26dc9bfa0b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/array-element-newline.js
@@ -0,0 +1,230 @@
+/**
+ * @fileoverview Rule to enforce line breaks after each array element
+ * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce line breaks after each array element",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ multiline: {
+ type: "boolean"
+ },
+ minItems: {
+ type: ["integer", "null"],
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Normalizes a given option value.
+ *
+ * @param {string|Object|undefined} option - An option value to parse.
+ * @returns {{multiline: boolean, minItems: number}} Normalized option object.
+ */
+ function normalizeOptionValue(option) {
+ let multiline = false;
+ let minItems;
+
+ option = option || "always";
+
+ if (option === "always" || option.minItems === 0) {
+ minItems = 0;
+ } else if (option === "never") {
+ minItems = Number.POSITIVE_INFINITY;
+ } else {
+ multiline = Boolean(option.multiline);
+ minItems = option.minItems || Number.POSITIVE_INFINITY;
+ }
+
+ return { multiline, minItems };
+ }
+
+ /**
+ * Normalizes a given option value.
+ *
+ * @param {string|Object|undefined} options - An option value to parse.
+ * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
+ */
+ function normalizeOptions(options) {
+ const value = normalizeOptionValue(options);
+
+ return { ArrayExpression: value, ArrayPattern: value };
+ }
+
+ /**
+ * Reports that there shouldn't be a line break after the first token
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoLineBreak(token) {
+ const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
+
+ context.report({
+ loc: {
+ start: tokenBefore.loc.end,
+ end: token.loc.start
+ },
+ message: "There should be no linebreak here.",
+ fix(fixer) {
+ if (astUtils.isCommentToken(tokenBefore)) {
+ return null;
+ }
+
+ if (!astUtils.isTokenOnSameLine(tokenBefore, token)) {
+ return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ");
+ }
+
+ /*
+ * This will check if the comma is on the same line as the next element
+ * Following array:
+ * [
+ * 1
+ * , 2
+ * , 3
+ * ]
+ *
+ * will be fixed to:
+ * [
+ * 1, 2, 3
+ * ]
+ */
+ const twoTokensBefore = sourceCode.getTokenBefore(tokenBefore, { includeComments: true });
+
+ if (astUtils.isCommentToken(twoTokensBefore)) {
+ return null;
+ }
+
+ return fixer.replaceTextRange([twoTokensBefore.range[1], tokenBefore.range[0]], "");
+
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a line break after the first token
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredLineBreak(token) {
+ const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
+
+ context.report({
+ loc: {
+ start: tokenBefore.loc.end,
+ end: token.loc.start
+ },
+ message: "There should be a linebreak after this element.",
+ fix(fixer) {
+ return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n");
+ }
+ });
+ }
+
+ /**
+ * Reports a given node if it violated this rule.
+ *
+ * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
+ * @param {{multiline: boolean, minItems: number}} options - An option object.
+ * @returns {void}
+ */
+ function check(node) {
+ const elements = node.elements;
+ const normalizedOptions = normalizeOptions(context.options[0]);
+ const options = normalizedOptions[node.type];
+
+ let elementBreak = false;
+
+ /*
+ * MULTILINE: true
+ * loop through every element and check
+ * if at least one element has linebreaks inside
+ * this ensures that following is not valid (due to elements are on the same line):
+ *
+ * [
+ * 1,
+ * 2,
+ * 3
+ * ]
+ */
+ if (options.multiline) {
+ elementBreak = elements
+ .filter(element => element !== null)
+ .some(element => element.loc.start.line !== element.loc.end.line);
+ }
+
+ const needsLinebreaks = (
+ elements.length >= options.minItems ||
+ (
+ options.multiline &&
+ elementBreak
+ )
+ );
+
+ elements.forEach((element, i) => {
+ const previousElement = elements[i - 1];
+
+ if (i === 0 || element === null || previousElement === null) {
+ return;
+ }
+
+ const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
+ const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
+ const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
+
+ if (needsLinebreaks) {
+ if (astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
+ reportRequiredLineBreak(firstTokenOfCurrentElement);
+ }
+ } else {
+ if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
+ reportNoLineBreak(firstTokenOfCurrentElement);
+ }
+ }
+ });
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ ArrayPattern: check,
+ ArrayExpression: check
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/arrow-body-style.js b/tools/node_modules/eslint/lib/rules/arrow-body-style.js
new file mode 100644
index 0000000000..78a391334d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/arrow-body-style.js
@@ -0,0 +1,215 @@
+/**
+ * @fileoverview Rule to require braces in arrow function body.
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require braces around arrow function bodies",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always", "never"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["as-needed"]
+ },
+ {
+ type: "object",
+ properties: {
+ requireReturnForObjectLiteral: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ },
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const options = context.options;
+ const always = options[0] === "always";
+ const asNeeded = !options[0] || options[0] === "as-needed";
+ const never = options[0] === "never";
+ const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Checks whether the given node has ASI problem or not.
+ * @param {Token} token The token to check.
+ * @returns {boolean} `true` if it changes semantics if `;` or `}` followed by the token are removed.
+ */
+ function hasASIProblem(token) {
+ return token && token.type === "Punctuator" && /^[([/`+-]/.test(token.value);
+ }
+
+ /**
+ * Gets the closing parenthesis which is the pair of the given opening parenthesis.
+ * @param {Token} token The opening parenthesis token to get.
+ * @returns {Token} The found closing parenthesis token.
+ */
+ function findClosingParen(token) {
+ let node = sourceCode.getNodeByRangeIndex(token.range[1]);
+
+ while (!astUtils.isParenthesised(sourceCode, node)) {
+ node = node.parent;
+ }
+ return sourceCode.getTokenAfter(node);
+ }
+
+ /**
+ * Determines whether a arrow function body needs braces
+ * @param {ASTNode} node The arrow function node.
+ * @returns {void}
+ */
+ function validate(node) {
+ const arrowBody = node.body;
+
+ if (arrowBody.type === "BlockStatement") {
+ const blockBody = arrowBody.body;
+
+ if (blockBody.length !== 1 && !never) {
+ return;
+ }
+
+ if (asNeeded && requireReturnForObjectLiteral && blockBody[0].type === "ReturnStatement" &&
+ blockBody[0].argument && blockBody[0].argument.type === "ObjectExpression") {
+ return;
+ }
+
+ if (never || asNeeded && blockBody[0].type === "ReturnStatement") {
+ context.report({
+ node,
+ loc: arrowBody.loc.start,
+ message: "Unexpected block statement surrounding arrow body.",
+ fix(fixer) {
+ const fixes = [];
+
+ if (blockBody.length !== 1 ||
+ blockBody[0].type !== "ReturnStatement" ||
+ !blockBody[0].argument ||
+ hasASIProblem(sourceCode.getTokenAfter(arrowBody))
+ ) {
+ return fixes;
+ }
+
+ const openingBrace = sourceCode.getFirstToken(arrowBody);
+ const closingBrace = sourceCode.getLastToken(arrowBody);
+ const firstValueToken = sourceCode.getFirstToken(blockBody[0], 1);
+ const lastValueToken = sourceCode.getLastToken(blockBody[0]);
+ const commentsExist =
+ sourceCode.commentsExistBetween(openingBrace, firstValueToken) ||
+ sourceCode.commentsExistBetween(lastValueToken, closingBrace);
+
+ /*
+ * Remove tokens around the return value.
+ * If comments don't exist, remove extra spaces as well.
+ */
+ if (commentsExist) {
+ fixes.push(
+ fixer.remove(openingBrace),
+ fixer.remove(closingBrace),
+ fixer.remove(sourceCode.getTokenAfter(openingBrace)) // return keyword
+ );
+ } else {
+ fixes.push(
+ fixer.removeRange([openingBrace.range[0], firstValueToken.range[0]]),
+ fixer.removeRange([lastValueToken.range[1], closingBrace.range[1]])
+ );
+ }
+
+ /*
+ * If the first token of the reutrn value is `{`,
+ * enclose the return value by parentheses to avoid syntax error.
+ */
+ if (astUtils.isOpeningBraceToken(firstValueToken)) {
+ fixes.push(
+ fixer.insertTextBefore(firstValueToken, "("),
+ fixer.insertTextAfter(lastValueToken, ")")
+ );
+ }
+
+ /*
+ * If the last token of the return statement is semicolon, remove it.
+ * Non-block arrow body is an expression, not a statement.
+ */
+ if (astUtils.isSemicolonToken(lastValueToken)) {
+ fixes.push(fixer.remove(lastValueToken));
+ }
+
+ return fixes;
+ }
+ });
+ }
+ } else {
+ if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
+ context.report({
+ node,
+ loc: arrowBody.loc.start,
+ message: "Expected block statement surrounding arrow body.",
+ fix(fixer) {
+ const fixes = [];
+ const arrowToken = sourceCode.getTokenBefore(arrowBody, astUtils.isArrowToken);
+ const firstBodyToken = sourceCode.getTokenAfter(arrowToken);
+ const lastBodyToken = sourceCode.getLastToken(node);
+ const isParenthesisedObjectLiteral =
+ astUtils.isOpeningParenToken(firstBodyToken) &&
+ astUtils.isOpeningBraceToken(sourceCode.getTokenAfter(firstBodyToken));
+
+ // Wrap the value by a block and a return statement.
+ fixes.push(
+ fixer.insertTextBefore(firstBodyToken, "{return "),
+ fixer.insertTextAfter(lastBodyToken, "}")
+ );
+
+ // If the value is object literal, remove parentheses which were forced by syntax.
+ if (isParenthesisedObjectLiteral) {
+ fixes.push(
+ fixer.remove(firstBodyToken),
+ fixer.remove(findClosingParen(firstBodyToken))
+ );
+ }
+
+ return fixes;
+ }
+ });
+ }
+ }
+ }
+
+ return {
+ "ArrowFunctionExpression:exit": validate
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/arrow-parens.js b/tools/node_modules/eslint/lib/rules/arrow-parens.js
new file mode 100644
index 0000000000..e8f8ddd8e7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/arrow-parens.js
@@ -0,0 +1,156 @@
+/**
+ * @fileoverview Rule to require parens in arrow function arguments.
+ * @author Jxck
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require parentheses around arrow function arguments",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: [
+ {
+ enum: ["always", "as-needed"]
+ },
+ {
+ type: "object",
+ properties: {
+ requireForBlockBody: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const message = "Expected parentheses around arrow function argument.";
+ const asNeededMessage = "Unexpected parentheses around single function argument.";
+ const asNeeded = context.options[0] === "as-needed";
+ const requireForBlockBodyMessage = "Unexpected parentheses around single function argument having a body with no curly braces";
+ const requireForBlockBodyNoParensMessage = "Expected parentheses around arrow function argument having a body with curly braces.";
+ const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether a arrow function argument end with `)`
+ * @param {ASTNode} node The arrow function node.
+ * @returns {void}
+ */
+ function parens(node) {
+ const isAsync = node.async;
+ const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0);
+
+ /**
+ * Remove the parenthesis around a parameter
+ * @param {Fixer} fixer Fixer
+ * @returns {string} fixed parameter
+ */
+ function fixParamsWithParenthesis(fixer) {
+ const paramToken = sourceCode.getTokenAfter(firstTokenOfParam);
+
+ /*
+ * ES8 allows Trailing commas in function parameter lists and calls
+ * https://github.com/eslint/eslint/issues/8834
+ */
+ const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken);
+ const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null;
+ const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]);
+
+ return fixer.replaceTextRange([
+ firstTokenOfParam.range[0],
+ closingParenToken.range[1]
+ ], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
+ }
+
+ // "as-needed", { "requireForBlockBody": true }: x => x
+ if (
+ requireForBlockBody &&
+ node.params.length === 1 &&
+ node.params[0].type === "Identifier" &&
+ !node.params[0].typeAnnotation &&
+ node.body.type !== "BlockStatement" &&
+ !node.returnType
+ ) {
+ if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
+ context.report({
+ node,
+ message: requireForBlockBodyMessage,
+ fix: fixParamsWithParenthesis
+ });
+ }
+ return;
+ }
+
+ if (
+ requireForBlockBody &&
+ node.body.type === "BlockStatement"
+ ) {
+ if (!astUtils.isOpeningParenToken(firstTokenOfParam)) {
+ context.report({
+ node,
+ message: requireForBlockBodyNoParensMessage,
+ fix(fixer) {
+ return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
+ }
+ });
+ }
+ return;
+ }
+
+ // "as-needed": x => x
+ if (asNeeded &&
+ node.params.length === 1 &&
+ node.params[0].type === "Identifier" &&
+ !node.params[0].typeAnnotation &&
+ !node.returnType
+ ) {
+ if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
+ context.report({
+ node,
+ message: asNeededMessage,
+ fix: fixParamsWithParenthesis
+ });
+ }
+ return;
+ }
+
+ if (firstTokenOfParam.type === "Identifier") {
+ const after = sourceCode.getTokenAfter(firstTokenOfParam);
+
+ // (x) => x
+ if (after.value !== ")") {
+ context.report({
+ node,
+ message,
+ fix(fixer) {
+ return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
+ }
+ });
+ }
+ }
+ }
+
+ return {
+ ArrowFunctionExpression: parens
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/arrow-spacing.js b/tools/node_modules/eslint/lib/rules/arrow-spacing.js
new file mode 100644
index 0000000000..37e03907cb
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/arrow-spacing.js
@@ -0,0 +1,149 @@
+/**
+ * @fileoverview Rule to define spacing before/after arrow function's arrow.
+ * @author Jxck
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before and after the arrow in arrow functions",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ before: {
+ type: "boolean"
+ },
+ after: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ // merge rules with default
+ const rule = { before: true, after: true },
+ option = context.options[0] || {};
+
+ rule.before = option.before !== false;
+ rule.after = option.after !== false;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Get tokens of arrow(`=>`) and before/after arrow.
+ * @param {ASTNode} node The arrow function node.
+ * @returns {Object} Tokens of arrow and before/after arrow.
+ */
+ function getTokens(node) {
+ const arrow = sourceCode.getTokenBefore(node.body, astUtils.isArrowToken);
+
+ return {
+ before: sourceCode.getTokenBefore(arrow),
+ arrow,
+ after: sourceCode.getTokenAfter(arrow)
+ };
+ }
+
+ /**
+ * Count spaces before/after arrow(`=>`) token.
+ * @param {Object} tokens Tokens before/after arrow.
+ * @returns {Object} count of space before/after arrow.
+ */
+ function countSpaces(tokens) {
+ const before = tokens.arrow.range[0] - tokens.before.range[1];
+ const after = tokens.after.range[0] - tokens.arrow.range[1];
+
+ return { before, after };
+ }
+
+ /**
+ * Determines whether space(s) before after arrow(`=>`) is satisfy rule.
+ * if before/after value is `true`, there should be space(s).
+ * if before/after value is `false`, there should be no space.
+ * @param {ASTNode} node The arrow function node.
+ * @returns {void}
+ */
+ function spaces(node) {
+ const tokens = getTokens(node);
+ const countSpace = countSpaces(tokens);
+
+ if (rule.before) {
+
+ // should be space(s) before arrow
+ if (countSpace.before === 0) {
+ context.report({
+ node: tokens.before,
+ message: "Missing space before =>.",
+ fix(fixer) {
+ return fixer.insertTextBefore(tokens.arrow, " ");
+ }
+ });
+ }
+ } else {
+
+ // should be no space before arrow
+ if (countSpace.before > 0) {
+ context.report({
+ node: tokens.before,
+ message: "Unexpected space before =>.",
+ fix(fixer) {
+ return fixer.removeRange([tokens.before.range[1], tokens.arrow.range[0]]);
+ }
+ });
+ }
+ }
+
+ if (rule.after) {
+
+ // should be space(s) after arrow
+ if (countSpace.after === 0) {
+ context.report({
+ node: tokens.after,
+ message: "Missing space after =>.",
+ fix(fixer) {
+ return fixer.insertTextAfter(tokens.arrow, " ");
+ }
+ });
+ }
+ } else {
+
+ // should be no space after arrow
+ if (countSpace.after > 0) {
+ context.report({
+ node: tokens.after,
+ message: "Unexpected space after =>.",
+ fix(fixer) {
+ return fixer.removeRange([tokens.arrow.range[1], tokens.after.range[0]]);
+ }
+ });
+ }
+ }
+ }
+
+ return {
+ ArrowFunctionExpression: spaces
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/block-scoped-var.js b/tools/node_modules/eslint/lib/rules/block-scoped-var.js
new file mode 100644
index 0000000000..0b4d855fae
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/block-scoped-var.js
@@ -0,0 +1,115 @@
+/**
+ * @fileoverview Rule to check for "block scoped" variables by binding context
+ * @author Matt DuVall <http://www.mattduvall.com>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the use of variables within the scope they are defined",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ let stack = [];
+
+ /**
+ * Makes a block scope.
+ * @param {ASTNode} node - A node of a scope.
+ * @returns {void}
+ */
+ function enterScope(node) {
+ stack.push(node.range);
+ }
+
+ /**
+ * Pops the last block scope.
+ * @returns {void}
+ */
+ function exitScope() {
+ stack.pop();
+ }
+
+ /**
+ * Reports a given reference.
+ * @param {eslint-scope.Reference} reference - A reference to report.
+ * @returns {void}
+ */
+ function report(reference) {
+ const identifier = reference.identifier;
+
+ context.report({ node: identifier, message: "'{{name}}' used outside of binding context.", data: { name: identifier.name } });
+ }
+
+ /**
+ * Finds and reports references which are outside of valid scopes.
+ * @param {ASTNode} node - A node to get variables.
+ * @returns {void}
+ */
+ function checkForVariables(node) {
+ if (node.kind !== "var") {
+ return;
+ }
+
+ // Defines a predicate to check whether or not a given reference is outside of valid scope.
+ const scopeRange = stack[stack.length - 1];
+
+ /**
+ * Check if a reference is out of scope
+ * @param {ASTNode} reference node to examine
+ * @returns {boolean} True is its outside the scope
+ * @private
+ */
+ function isOutsideOfScope(reference) {
+ const idRange = reference.identifier.range;
+
+ return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
+ }
+
+ // Gets declared variables, and checks its references.
+ const variables = context.getDeclaredVariables(node);
+
+ for (let i = 0; i < variables.length; ++i) {
+
+ // Reports.
+ variables[i]
+ .references
+ .filter(isOutsideOfScope)
+ .forEach(report);
+ }
+ }
+
+ return {
+ Program(node) {
+ stack = [node.range];
+ },
+
+ // Manages scopes.
+ BlockStatement: enterScope,
+ "BlockStatement:exit": exitScope,
+ ForStatement: enterScope,
+ "ForStatement:exit": exitScope,
+ ForInStatement: enterScope,
+ "ForInStatement:exit": exitScope,
+ ForOfStatement: enterScope,
+ "ForOfStatement:exit": exitScope,
+ SwitchStatement: enterScope,
+ "SwitchStatement:exit": exitScope,
+ CatchClause: enterScope,
+ "CatchClause:exit": exitScope,
+
+ // Finds and reports references which are outside of valid scope.
+ VariableDeclaration: checkForVariables
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/block-spacing.js b/tools/node_modules/eslint/lib/rules/block-spacing.js
new file mode 100644
index 0000000000..b3ea8405e2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/block-spacing.js
@@ -0,0 +1,137 @@
+/**
+ * @fileoverview A rule to disallow or enforce spaces inside of single line blocks.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const util = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ { enum: ["always", "never"] }
+ ]
+ },
+
+ create(context) {
+ const always = (context.options[0] !== "never"),
+ message = always ? "Requires a space" : "Unexpected space(s)",
+ sourceCode = context.getSourceCode();
+
+ /**
+ * Gets the open brace token from a given node.
+ * @param {ASTNode} node - A BlockStatement/SwitchStatement node to get.
+ * @returns {Token} The token of the open brace.
+ */
+ function getOpenBrace(node) {
+ if (node.type === "SwitchStatement") {
+ if (node.cases.length > 0) {
+ return sourceCode.getTokenBefore(node.cases[0]);
+ }
+ return sourceCode.getLastToken(node, 1);
+ }
+ return sourceCode.getFirstToken(node);
+ }
+
+ /**
+ * Checks whether or not:
+ * - given tokens are on same line.
+ * - there is/isn't a space between given tokens.
+ * @param {Token} left - A token to check.
+ * @param {Token} right - The token which is next to `left`.
+ * @returns {boolean}
+ * When the option is `"always"`, `true` if there are one or more spaces between given tokens.
+ * When the option is `"never"`, `true` if there are not any spaces between given tokens.
+ * If given tokens are not on same line, it's always `true`.
+ */
+ function isValid(left, right) {
+ return (
+ !util.isTokenOnSameLine(left, right) ||
+ sourceCode.isSpaceBetweenTokens(left, right) === always
+ );
+ }
+
+ /**
+ * Reports invalid spacing style inside braces.
+ * @param {ASTNode} node - A BlockStatement/SwitchStatement node to get.
+ * @returns {void}
+ */
+ function checkSpacingInsideBraces(node) {
+
+ // Gets braces and the first/last token of content.
+ const openBrace = getOpenBrace(node);
+ const closeBrace = sourceCode.getLastToken(node);
+ const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
+ const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
+
+ // Skip if the node is invalid or empty.
+ if (openBrace.type !== "Punctuator" ||
+ openBrace.value !== "{" ||
+ closeBrace.type !== "Punctuator" ||
+ closeBrace.value !== "}" ||
+ firstToken === closeBrace
+ ) {
+ return;
+ }
+
+ // Skip line comments for option never
+ if (!always && firstToken.type === "Line") {
+ return;
+ }
+
+ // Check.
+ if (!isValid(openBrace, firstToken)) {
+ context.report({
+ node,
+ loc: openBrace.loc.start,
+ message: "{{message}} after '{'.",
+ data: {
+ message
+ },
+ fix(fixer) {
+ if (always) {
+ return fixer.insertTextBefore(firstToken, " ");
+ }
+
+ return fixer.removeRange([openBrace.range[1], firstToken.range[0]]);
+ }
+ });
+ }
+ if (!isValid(lastToken, closeBrace)) {
+ context.report({
+ node,
+ loc: closeBrace.loc.start,
+ message: "{{message}} before '}'.",
+ data: {
+ message
+ },
+ fix(fixer) {
+ if (always) {
+ return fixer.insertTextAfter(lastToken, " ");
+ }
+
+ return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]);
+ }
+ });
+ }
+ }
+
+ return {
+ BlockStatement: checkSpacingInsideBraces,
+ SwitchStatement: checkSpacingInsideBraces
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/brace-style.js b/tools/node_modules/eslint/lib/rules/brace-style.js
new file mode 100644
index 0000000000..320da9dac9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/brace-style.js
@@ -0,0 +1,182 @@
+/**
+ * @fileoverview Rule to flag block statements that do not use the one true brace style
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent brace style for blocks",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["1tbs", "stroustrup", "allman"]
+ },
+ {
+ type: "object",
+ properties: {
+ allowSingleLine: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "whitespace"
+ },
+
+ create(context) {
+ const style = context.options[0] || "1tbs",
+ params = context.options[1] || {},
+ sourceCode = context.getSourceCode();
+
+ const OPEN_MESSAGE = "Opening curly brace does not appear on the same line as controlling statement.",
+ OPEN_MESSAGE_ALLMAN = "Opening curly brace appears on the same line as controlling statement.",
+ BODY_MESSAGE = "Statement inside of curly braces should be on next line.",
+ CLOSE_MESSAGE = "Closing curly brace does not appear on the same line as the subsequent block.",
+ CLOSE_MESSAGE_SINGLE = "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
+ CLOSE_MESSAGE_STROUSTRUP_ALLMAN = "Closing curly brace appears on the same line as the subsequent block.";
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Fixes a place where a newline unexpectedly appears
+ * @param {Token} firstToken The token before the unexpected newline
+ * @param {Token} secondToken The token after the unexpected newline
+ * @returns {Function} A fixer function to remove the newlines between the tokens
+ */
+ function removeNewlineBetween(firstToken, secondToken) {
+ const textRange = [firstToken.range[1], secondToken.range[0]];
+ const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);
+
+ // Don't do a fix if there is a comment between the tokens
+ if (textBetween.trim()) {
+ return null;
+ }
+ return fixer => fixer.replaceTextRange(textRange, " ");
+ }
+
+ /**
+ * Validates a pair of curly brackets based on the user's config
+ * @param {Token} openingCurly The opening curly bracket
+ * @param {Token} closingCurly The closing curly bracket
+ * @returns {void}
+ */
+ function validateCurlyPair(openingCurly, closingCurly) {
+ const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurly);
+ const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurly);
+ const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurly);
+ const singleLineException = params.allowSingleLine && astUtils.isTokenOnSameLine(openingCurly, closingCurly);
+
+ if (style !== "allman" && !astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly)) {
+ context.report({
+ node: openingCurly,
+ message: OPEN_MESSAGE,
+ fix: removeNewlineBetween(tokenBeforeOpeningCurly, openingCurly)
+ });
+ }
+
+ if (style === "allman" && astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly) && !singleLineException) {
+ context.report({
+ node: openingCurly,
+ message: OPEN_MESSAGE_ALLMAN,
+ fix: fixer => fixer.insertTextBefore(openingCurly, "\n")
+ });
+ }
+
+ if (astUtils.isTokenOnSameLine(openingCurly, tokenAfterOpeningCurly) && tokenAfterOpeningCurly !== closingCurly && !singleLineException) {
+ context.report({
+ node: openingCurly,
+ message: BODY_MESSAGE,
+ fix: fixer => fixer.insertTextAfter(openingCurly, "\n")
+ });
+ }
+
+ if (tokenBeforeClosingCurly !== openingCurly && !singleLineException && astUtils.isTokenOnSameLine(tokenBeforeClosingCurly, closingCurly)) {
+ context.report({
+ node: closingCurly,
+ message: CLOSE_MESSAGE_SINGLE,
+ fix: fixer => fixer.insertTextBefore(closingCurly, "\n")
+ });
+ }
+ }
+
+ /**
+ * Validates the location of a token that appears before a keyword (e.g. a newline before `else`)
+ * @param {Token} curlyToken The closing curly token. This is assumed to precede a keyword token (such as `else` or `finally`).
+ * @returns {void}
+ */
+ function validateCurlyBeforeKeyword(curlyToken) {
+ const keywordToken = sourceCode.getTokenAfter(curlyToken);
+
+ if (style === "1tbs" && !astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
+ context.report({
+ node: curlyToken,
+ message: CLOSE_MESSAGE,
+ fix: removeNewlineBetween(curlyToken, keywordToken)
+ });
+ }
+
+ if (style !== "1tbs" && astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
+ context.report({
+ node: curlyToken,
+ message: CLOSE_MESSAGE_STROUSTRUP_ALLMAN,
+ fix: fixer => fixer.insertTextAfter(curlyToken, "\n")
+ });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ BlockStatement(node) {
+ if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
+ validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
+ }
+ },
+ ClassBody(node) {
+ validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
+ },
+ SwitchStatement(node) {
+ const closingCurly = sourceCode.getLastToken(node);
+ const openingCurly = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly);
+
+ validateCurlyPair(openingCurly, closingCurly);
+ },
+ IfStatement(node) {
+ if (node.consequent.type === "BlockStatement" && node.alternate) {
+
+ // Handle the keyword after the `if` block (before `else`)
+ validateCurlyBeforeKeyword(sourceCode.getLastToken(node.consequent));
+ }
+ },
+ TryStatement(node) {
+
+ // Handle the keyword after the `try` block (before `catch` or `finally`)
+ validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));
+
+ if (node.handler && node.finalizer) {
+
+ // Handle the keyword after the `catch` block (before `finally`)
+ validateCurlyBeforeKeyword(sourceCode.getLastToken(node.handler.body));
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/callback-return.js b/tools/node_modules/eslint/lib/rules/callback-return.js
new file mode 100644
index 0000000000..0fa7f278a1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/callback-return.js
@@ -0,0 +1,175 @@
+/**
+ * @fileoverview Enforce return after a callback.
+ * @author Jamund Ferguson
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `return` statements after callbacks",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: [{
+ type: "array",
+ items: { type: "string" }
+ }]
+ },
+
+ create(context) {
+
+ const callbacks = context.options[0] || ["callback", "cb", "next"],
+ sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Find the closest parent matching a list of types.
+ * @param {ASTNode} node The node whose parents we are searching
+ * @param {Array} types The node types to match
+ * @returns {ASTNode} The matched node or undefined.
+ */
+ function findClosestParentOfType(node, types) {
+ if (!node.parent) {
+ return null;
+ }
+ if (types.indexOf(node.parent.type) === -1) {
+ return findClosestParentOfType(node.parent, types);
+ }
+ return node.parent;
+ }
+
+ /**
+ * Check to see if a node contains only identifers
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} Whether or not the node contains only identifers
+ */
+ function containsOnlyIdentifiers(node) {
+ if (node.type === "Identifier") {
+ return true;
+ }
+
+ if (node.type === "MemberExpression") {
+ if (node.object.type === "Identifier") {
+ return true;
+ }
+ if (node.object.type === "MemberExpression") {
+ return containsOnlyIdentifiers(node.object);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check to see if a CallExpression is in our callback list.
+ * @param {ASTNode} node The node to check against our callback names list.
+ * @returns {boolean} Whether or not this function matches our callback name.
+ */
+ function isCallback(node) {
+ return containsOnlyIdentifiers(node.callee) && callbacks.indexOf(sourceCode.getText(node.callee)) > -1;
+ }
+
+ /**
+ * Determines whether or not the callback is part of a callback expression.
+ * @param {ASTNode} node The callback node
+ * @param {ASTNode} parentNode The expression node
+ * @returns {boolean} Whether or not this is part of a callback expression
+ */
+ function isCallbackExpression(node, parentNode) {
+
+ // ensure the parent node exists and is an expression
+ if (!parentNode || parentNode.type !== "ExpressionStatement") {
+ return false;
+ }
+
+ // cb()
+ if (parentNode.expression === node) {
+ return true;
+ }
+
+ // special case for cb && cb() and similar
+ if (parentNode.expression.type === "BinaryExpression" || parentNode.expression.type === "LogicalExpression") {
+ if (parentNode.expression.right === node) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ CallExpression(node) {
+
+ // if we're not a callback we can return
+ if (!isCallback(node)) {
+ return;
+ }
+
+ // find the closest block, return or loop
+ const closestBlock = findClosestParentOfType(node, ["BlockStatement", "ReturnStatement", "ArrowFunctionExpression"]) || {};
+
+ // if our parent is a return we know we're ok
+ if (closestBlock.type === "ReturnStatement") {
+ return;
+ }
+
+ // arrow functions don't always have blocks and implicitly return
+ if (closestBlock.type === "ArrowFunctionExpression") {
+ return;
+ }
+
+ // block statements are part of functions and most if statements
+ if (closestBlock.type === "BlockStatement") {
+
+ // find the last item in the block
+ const lastItem = closestBlock.body[closestBlock.body.length - 1];
+
+ // if the callback is the last thing in a block that might be ok
+ if (isCallbackExpression(node, lastItem)) {
+
+ const parentType = closestBlock.parent.type;
+
+ // but only if the block is part of a function
+ if (parentType === "FunctionExpression" ||
+ parentType === "FunctionDeclaration" ||
+ parentType === "ArrowFunctionExpression"
+ ) {
+ return;
+ }
+
+ }
+
+ // ending a block with a return is also ok
+ if (lastItem.type === "ReturnStatement") {
+
+ // but only if the callback is immediately before
+ if (isCallbackExpression(node, closestBlock.body[closestBlock.body.length - 2])) {
+ return;
+ }
+ }
+
+ }
+
+ // as long as you're the child of a function at this point you should be asked to return
+ if (findClosestParentOfType(node, ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"])) {
+ context.report({ node, message: "Expected return with your callback function." });
+ }
+
+ }
+
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/camelcase.js b/tools/node_modules/eslint/lib/rules/camelcase.js
new file mode 100644
index 0000000000..6fb1475b21
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/camelcase.js
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview Rule to flag non-camelcased identifiers
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce camelcase naming convention",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ properties: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
+ const reported = [];
+ const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
+
+ /**
+ * Checks if a string contains an underscore and isn't all upper-case
+ * @param {string} name The string to check.
+ * @returns {boolean} if the string is underscored
+ * @private
+ */
+ function isUnderscored(name) {
+
+ // if there's an underscore, it might be A_CONSTANT, which is okay
+ return name.indexOf("_") > -1 && name !== name.toUpperCase();
+ }
+
+ /**
+ * Reports an AST node as a rule violation.
+ * @param {ASTNode} node The node to report.
+ * @returns {void}
+ * @private
+ */
+ function report(node) {
+ if (reported.indexOf(node) < 0) {
+ reported.push(node);
+ context.report({ node, message: "Identifier '{{name}}' is not in camel case.", data: { name: node.name } });
+ }
+ }
+
+ const options = context.options[0] || {};
+ let properties = options.properties || "";
+
+ if (properties !== "always" && properties !== "never") {
+ properties = "always";
+ }
+
+ return {
+
+ Identifier(node) {
+
+ /*
+ * Leading and trailing underscores are commonly used to flag
+ * private/protected identifiers, strip them
+ */
+ const name = node.name.replace(/^_+|_+$/g, ""),
+ effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
+
+ // MemberExpressions get special rules
+ if (node.parent.type === "MemberExpression") {
+
+ // "never" check properties
+ if (properties === "never") {
+ return;
+ }
+
+ // Always report underscored object names
+ if (node.parent.object.type === "Identifier" &&
+ node.parent.object.name === node.name &&
+ isUnderscored(name)) {
+ report(node);
+
+ // Report AssignmentExpressions only if they are the left side of the assignment
+ } else if (effectiveParent.type === "AssignmentExpression" &&
+ isUnderscored(name) &&
+ (effectiveParent.right.type !== "MemberExpression" ||
+ effectiveParent.left.type === "MemberExpression" &&
+ effectiveParent.left.property.name === node.name)) {
+ report(node);
+ }
+
+ // Properties have their own rules
+ } else if (node.parent.type === "Property") {
+
+ // "never" check properties
+ if (properties === "never") {
+ return;
+ }
+
+ if (node.parent.parent && node.parent.parent.type === "ObjectPattern" &&
+ node.parent.key === node && node.parent.value !== node) {
+ return;
+ }
+
+ if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
+ report(node);
+ }
+
+ // Check if it's an import specifier
+ } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) {
+
+ // Report only if the local imported identifier is underscored
+ if (node.parent.local && node.parent.local.name === node.name && isUnderscored(name)) {
+ report(node);
+ }
+
+ // Report anything that is underscored that isn't a CallExpression
+ } else if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
+ report(node);
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/capitalized-comments.js b/tools/node_modules/eslint/lib/rules/capitalized-comments.js
new file mode 100644
index 0000000000..1a27608067
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/capitalized-comments.js
@@ -0,0 +1,303 @@
+/**
+ * @fileoverview enforce or disallow capitalization of the first letter of a comment
+ * @author Kevin Partington
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const LETTER_PATTERN = require("../util/patterns/letters");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const ALWAYS_MESSAGE = "Comments should not begin with a lowercase character",
+ NEVER_MESSAGE = "Comments should not begin with an uppercase character",
+ DEFAULT_IGNORE_PATTERN = astUtils.COMMENTS_IGNORE_PATTERN,
+ WHITESPACE = /\s/g,
+ MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/, // TODO: Combine w/ max-len pattern?
+ DEFAULTS = {
+ ignorePattern: null,
+ ignoreInlineComments: false,
+ ignoreConsecutiveComments: false
+ };
+
+/*
+ * Base schema body for defining the basic capitalization rule, ignorePattern,
+ * and ignoreInlineComments values.
+ * This can be used in a few different ways in the actual schema.
+ */
+const SCHEMA_BODY = {
+ type: "object",
+ properties: {
+ ignorePattern: {
+ type: "string"
+ },
+ ignoreInlineComments: {
+ type: "boolean"
+ },
+ ignoreConsecutiveComments: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+};
+
+/**
+ * Get normalized options for either block or line comments from the given
+ * user-provided options.
+ * - If the user-provided options is just a string, returns a normalized
+ * set of options using default values for all other options.
+ * - If the user-provided options is an object, then a normalized option
+ * set is returned. Options specified in overrides will take priority
+ * over options specified in the main options object, which will in
+ * turn take priority over the rule's defaults.
+ *
+ * @param {Object|string} rawOptions The user-provided options.
+ * @param {string} which Either "line" or "block".
+ * @returns {Object} The normalized options.
+ */
+function getNormalizedOptions(rawOptions, which) {
+ if (!rawOptions) {
+ return Object.assign({}, DEFAULTS);
+ }
+
+ return Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
+}
+
+/**
+ * Get normalized options for block and line comments.
+ *
+ * @param {Object|string} rawOptions The user-provided options.
+ * @returns {Object} An object with "Line" and "Block" keys and corresponding
+ * normalized options objects.
+ */
+function getAllNormalizedOptions(rawOptions) {
+ return {
+ Line: getNormalizedOptions(rawOptions, "line"),
+ Block: getNormalizedOptions(rawOptions, "block")
+ };
+}
+
+/**
+ * Creates a regular expression for each ignorePattern defined in the rule
+ * options.
+ *
+ * This is done in order to avoid invoking the RegExp constructor repeatedly.
+ *
+ * @param {Object} normalizedOptions The normalized rule options.
+ * @returns {void}
+ */
+function createRegExpForIgnorePatterns(normalizedOptions) {
+ Object.keys(normalizedOptions).forEach(key => {
+ const ignorePatternStr = normalizedOptions[key].ignorePattern;
+
+ if (ignorePatternStr) {
+ const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`);
+
+ normalizedOptions[key].ignorePatternRegExp = regExp;
+ }
+ });
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce or disallow capitalization of the first letter of a comment",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "code",
+ schema: [
+ { enum: ["always", "never"] },
+ {
+ oneOf: [
+ SCHEMA_BODY,
+ {
+ type: "object",
+ properties: {
+ line: SCHEMA_BODY,
+ block: SCHEMA_BODY
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const capitalize = context.options[0] || "always",
+ normalizedOptions = getAllNormalizedOptions(context.options[1]),
+ sourceCode = context.getSourceCode();
+
+ createRegExpForIgnorePatterns(normalizedOptions);
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Checks whether a comment is an inline comment.
+ *
+ * For the purpose of this rule, a comment is inline if:
+ * 1. The comment is preceded by a token on the same line; and
+ * 2. The command is followed by a token on the same line.
+ *
+ * Note that the comment itself need not be single-line!
+ *
+ * Also, it follows from this definition that only block comments can
+ * be considered as possibly inline. This is because line comments
+ * would consume any following tokens on the same line as the comment.
+ *
+ * @param {ASTNode} comment The comment node to check.
+ * @returns {boolean} True if the comment is an inline comment, false
+ * otherwise.
+ */
+ function isInlineComment(comment) {
+ const previousToken = sourceCode.getTokenBefore(comment, { includeComments: true }),
+ nextToken = sourceCode.getTokenAfter(comment, { includeComments: true });
+
+ return Boolean(
+ previousToken &&
+ nextToken &&
+ comment.loc.start.line === previousToken.loc.end.line &&
+ comment.loc.end.line === nextToken.loc.start.line
+ );
+ }
+
+ /**
+ * Determine if a comment follows another comment.
+ *
+ * @param {ASTNode} comment The comment to check.
+ * @returns {boolean} True if the comment follows a valid comment.
+ */
+ function isConsecutiveComment(comment) {
+ const previousTokenOrComment = sourceCode.getTokenBefore(comment, { includeComments: true });
+
+ return Boolean(
+ previousTokenOrComment &&
+ ["Block", "Line"].indexOf(previousTokenOrComment.type) !== -1
+ );
+ }
+
+ /**
+ * Check a comment to determine if it is valid for this rule.
+ *
+ * @param {ASTNode} comment The comment node to process.
+ * @param {Object} options The options for checking this comment.
+ * @returns {boolean} True if the comment is valid, false otherwise.
+ */
+ function isCommentValid(comment, options) {
+
+ // 1. Check for default ignore pattern.
+ if (DEFAULT_IGNORE_PATTERN.test(comment.value)) {
+ return true;
+ }
+
+ // 2. Check for custom ignore pattern.
+ const commentWithoutAsterisks = comment.value
+ .replace(/\*/g, "");
+
+ if (options.ignorePatternRegExp && options.ignorePatternRegExp.test(commentWithoutAsterisks)) {
+ return true;
+ }
+
+ // 3. Check for inline comments.
+ if (options.ignoreInlineComments && isInlineComment(comment)) {
+ return true;
+ }
+
+ // 4. Is this a consecutive comment (and are we tolerating those)?
+ if (options.ignoreConsecutiveComments && isConsecutiveComment(comment)) {
+ return true;
+ }
+
+ // 5. Does the comment start with a possible URL?
+ if (MAYBE_URL.test(commentWithoutAsterisks)) {
+ return true;
+ }
+
+ // 6. Is the initial word character a letter?
+ const commentWordCharsOnly = commentWithoutAsterisks
+ .replace(WHITESPACE, "");
+
+ if (commentWordCharsOnly.length === 0) {
+ return true;
+ }
+
+ const firstWordChar = commentWordCharsOnly[0];
+
+ if (!LETTER_PATTERN.test(firstWordChar)) {
+ return true;
+ }
+
+ // 7. Check the case of the initial word character.
+ const isUppercase = firstWordChar !== firstWordChar.toLocaleLowerCase(),
+ isLowercase = firstWordChar !== firstWordChar.toLocaleUpperCase();
+
+ if (capitalize === "always" && isLowercase) {
+ return false;
+ }
+ if (capitalize === "never" && isUppercase) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Process a comment to determine if it needs to be reported.
+ *
+ * @param {ASTNode} comment The comment node to process.
+ * @returns {void}
+ */
+ function processComment(comment) {
+ const options = normalizedOptions[comment.type],
+ commentValid = isCommentValid(comment, options);
+
+ if (!commentValid) {
+ const message = capitalize === "always"
+ ? ALWAYS_MESSAGE
+ : NEVER_MESSAGE;
+
+ context.report({
+ node: null, // Intentionally using loc instead
+ loc: comment.loc,
+ message,
+ fix(fixer) {
+ const match = comment.value.match(LETTER_PATTERN);
+
+ return fixer.replaceTextRange(
+
+ // Offset match.index by 2 to account for the first 2 characters that start the comment (// or /*)
+ [comment.range[0] + match.index + 2, comment.range[0] + match.index + 3],
+ capitalize === "always" ? match[0].toLocaleUpperCase() : match[0].toLocaleLowerCase()
+ );
+ }
+ });
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ Program() {
+ const comments = sourceCode.getAllComments();
+
+ comments.filter(token => token.type !== "Shebang").forEach(processComment);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/class-methods-use-this.js b/tools/node_modules/eslint/lib/rules/class-methods-use-this.js
new file mode 100644
index 0000000000..d429c579b9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/class-methods-use-this.js
@@ -0,0 +1,110 @@
+/**
+ * @fileoverview Rule to enforce that all class methods use 'this'.
+ * @author Patrick Williams
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce that class methods utilize `this`",
+ category: "Best Practices",
+ recommended: false
+ },
+ schema: [{
+ type: "object",
+ properties: {
+ exceptMethods: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+ create(context) {
+ const config = context.options[0] ? Object.assign({}, context.options[0]) : {};
+ const exceptMethods = new Set(config.exceptMethods || []);
+
+ const stack = [];
+
+ /**
+ * Initializes the current context to false and pushes it onto the stack.
+ * These booleans represent whether 'this' has been used in the context.
+ * @returns {void}
+ * @private
+ */
+ function enterFunction() {
+ stack.push(false);
+ }
+
+ /**
+ * Check if the node is an instance method
+ * @param {ASTNode} node - node to check
+ * @returns {boolean} True if its an instance method
+ * @private
+ */
+ function isInstanceMethod(node) {
+ return !node.static && node.kind !== "constructor" && node.type === "MethodDefinition";
+ }
+
+ /**
+ * Check if the node is an instance method not excluded by config
+ * @param {ASTNode} node - node to check
+ * @returns {boolean} True if it is an instance method, and not excluded by config
+ * @private
+ */
+ function isIncludedInstanceMethod(node) {
+ return isInstanceMethod(node) && !exceptMethods.has(node.key.name);
+ }
+
+ /**
+ * Checks if we are leaving a function that is a method, and reports if 'this' has not been used.
+ * Static methods and the constructor are exempt.
+ * Then pops the context off the stack.
+ * @param {ASTNode} node - A function node that was entered.
+ * @returns {void}
+ * @private
+ */
+ function exitFunction(node) {
+ const methodUsesThis = stack.pop();
+
+ if (isIncludedInstanceMethod(node.parent) && !methodUsesThis) {
+ context.report({
+ node,
+ message: "Expected 'this' to be used by class method '{{classMethod}}'.",
+ data: {
+ classMethod: node.parent.key.name
+ }
+ });
+ }
+ }
+
+ /**
+ * Mark the current context as having used 'this'.
+ * @returns {void}
+ * @private
+ */
+ function markThisUsed() {
+ if (stack.length) {
+ stack[stack.length - 1] = true;
+ }
+ }
+
+ return {
+ FunctionDeclaration: enterFunction,
+ "FunctionDeclaration:exit": exitFunction,
+ FunctionExpression: enterFunction,
+ "FunctionExpression:exit": exitFunction,
+ ThisExpression: markThisUsed,
+ Super: markThisUsed
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/comma-dangle.js b/tools/node_modules/eslint/lib/rules/comma-dangle.js
new file mode 100644
index 0000000000..ddcc0cd229
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/comma-dangle.js
@@ -0,0 +1,337 @@
+/**
+ * @fileoverview Rule to forbid or enforce dangling commas.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const DEFAULT_OPTIONS = Object.freeze({
+ arrays: "never",
+ objects: "never",
+ imports: "never",
+ exports: "never",
+ functions: "ignore"
+});
+
+/**
+ * Checks whether or not a trailing comma is allowed in a given node.
+ * If the `lastItem` is `RestElement` or `RestProperty`, it disallows trailing commas.
+ *
+ * @param {ASTNode} lastItem - The node of the last element in the given node.
+ * @returns {boolean} `true` if a trailing comma is allowed.
+ */
+function isTrailingCommaAllowed(lastItem) {
+ return !(
+ lastItem.type === "RestElement" ||
+ lastItem.type === "RestProperty" ||
+ lastItem.type === "ExperimentalRestProperty"
+ );
+}
+
+/**
+ * Normalize option value.
+ *
+ * @param {string|Object|undefined} optionValue - The 1st option value to normalize.
+ * @returns {Object} The normalized option value.
+ */
+function normalizeOptions(optionValue) {
+ if (typeof optionValue === "string") {
+ return {
+ arrays: optionValue,
+ objects: optionValue,
+ imports: optionValue,
+ exports: optionValue,
+
+ // For backward compatibility, always ignore functions.
+ functions: "ignore"
+ };
+ }
+ if (typeof optionValue === "object" && optionValue !== null) {
+ return {
+ arrays: optionValue.arrays || DEFAULT_OPTIONS.arrays,
+ objects: optionValue.objects || DEFAULT_OPTIONS.objects,
+ imports: optionValue.imports || DEFAULT_OPTIONS.imports,
+ exports: optionValue.exports || DEFAULT_OPTIONS.exports,
+ functions: optionValue.functions || DEFAULT_OPTIONS.functions
+ };
+ }
+
+ return DEFAULT_OPTIONS;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow trailing commas",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "code",
+ schema: {
+ definitions: {
+ value: {
+ enum: [
+ "always-multiline",
+ "always",
+ "never",
+ "only-multiline"
+ ]
+ },
+ valueWithIgnore: {
+ enum: [
+ "always-multiline",
+ "always",
+ "ignore",
+ "never",
+ "only-multiline"
+ ]
+ }
+ },
+ type: "array",
+ items: [
+ {
+ oneOf: [
+ {
+ $ref: "#/definitions/value"
+ },
+ {
+ type: "object",
+ properties: {
+ arrays: { $ref: "#/definitions/valueWithIgnore" },
+ objects: { $ref: "#/definitions/valueWithIgnore" },
+ imports: { $ref: "#/definitions/valueWithIgnore" },
+ exports: { $ref: "#/definitions/valueWithIgnore" },
+ functions: { $ref: "#/definitions/valueWithIgnore" }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ }
+ },
+
+ create(context) {
+ const options = normalizeOptions(context.options[0]);
+ const sourceCode = context.getSourceCode();
+ const UNEXPECTED_MESSAGE = "Unexpected trailing comma.";
+ const MISSING_MESSAGE = "Missing trailing comma.";
+
+ /**
+ * Gets the last item of the given node.
+ * @param {ASTNode} node - The node to get.
+ * @returns {ASTNode|null} The last node or null.
+ */
+ function getLastItem(node) {
+ switch (node.type) {
+ case "ObjectExpression":
+ case "ObjectPattern":
+ return lodash.last(node.properties);
+ case "ArrayExpression":
+ case "ArrayPattern":
+ return lodash.last(node.elements);
+ case "ImportDeclaration":
+ case "ExportNamedDeclaration":
+ return lodash.last(node.specifiers);
+ case "FunctionDeclaration":
+ case "FunctionExpression":
+ case "ArrowFunctionExpression":
+ return lodash.last(node.params);
+ case "CallExpression":
+ case "NewExpression":
+ return lodash.last(node.arguments);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets the trailing comma token of the given node.
+ * If the trailing comma does not exist, this returns the token which is
+ * the insertion point of the trailing comma token.
+ *
+ * @param {ASTNode} node - The node to get.
+ * @param {ASTNode} lastItem - The last item of the node.
+ * @returns {Token} The trailing comma token or the insertion point.
+ */
+ function getTrailingToken(node, lastItem) {
+ switch (node.type) {
+ case "ObjectExpression":
+ case "ArrayExpression":
+ case "CallExpression":
+ case "NewExpression":
+ return sourceCode.getLastToken(node, 1);
+ default: {
+ const nextToken = sourceCode.getTokenAfter(lastItem);
+
+ if (astUtils.isCommaToken(nextToken)) {
+ return nextToken;
+ }
+ return sourceCode.getLastToken(lastItem);
+ }
+ }
+ }
+
+ /**
+ * Checks whether or not a given node is multiline.
+ * This rule handles a given node as multiline when the closing parenthesis
+ * and the last element are not on the same line.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is multiline.
+ */
+ function isMultiline(node) {
+ const lastItem = getLastItem(node);
+
+ if (!lastItem) {
+ return false;
+ }
+
+ const penultimateToken = getTrailingToken(node, lastItem);
+ const lastToken = sourceCode.getTokenAfter(penultimateToken);
+
+ return lastToken.loc.end.line !== penultimateToken.loc.end.line;
+ }
+
+ /**
+ * Reports a trailing comma if it exists.
+ *
+ * @param {ASTNode} node - A node to check. Its type is one of
+ * ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
+ * ImportDeclaration, and ExportNamedDeclaration.
+ * @returns {void}
+ */
+ function forbidTrailingComma(node) {
+ const lastItem = getLastItem(node);
+
+ if (!lastItem || (node.type === "ImportDeclaration" && lastItem.type !== "ImportSpecifier")) {
+ return;
+ }
+
+ const trailingToken = getTrailingToken(node, lastItem);
+
+ if (astUtils.isCommaToken(trailingToken)) {
+ context.report({
+ node: lastItem,
+ loc: trailingToken.loc.start,
+ message: UNEXPECTED_MESSAGE,
+ fix(fixer) {
+ return fixer.remove(trailingToken);
+ }
+ });
+ }
+ }
+
+ /**
+ * Reports the last element of a given node if it does not have a trailing
+ * comma.
+ *
+ * If a given node is `ArrayPattern` which has `RestElement`, the trailing
+ * comma is disallowed, so report if it exists.
+ *
+ * @param {ASTNode} node - A node to check. Its type is one of
+ * ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
+ * ImportDeclaration, and ExportNamedDeclaration.
+ * @returns {void}
+ */
+ function forceTrailingComma(node) {
+ const lastItem = getLastItem(node);
+
+ if (!lastItem || (node.type === "ImportDeclaration" && lastItem.type !== "ImportSpecifier")) {
+ return;
+ }
+ if (!isTrailingCommaAllowed(lastItem)) {
+ forbidTrailingComma(node);
+ return;
+ }
+
+ const trailingToken = getTrailingToken(node, lastItem);
+
+ if (trailingToken.value !== ",") {
+ context.report({
+ node: lastItem,
+ loc: trailingToken.loc.end,
+ message: MISSING_MESSAGE,
+ fix(fixer) {
+ return fixer.insertTextAfter(trailingToken, ",");
+ }
+ });
+ }
+ }
+
+ /**
+ * If a given node is multiline, reports the last element of a given node
+ * when it does not have a trailing comma.
+ * Otherwise, reports a trailing comma if it exists.
+ *
+ * @param {ASTNode} node - A node to check. Its type is one of
+ * ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
+ * ImportDeclaration, and ExportNamedDeclaration.
+ * @returns {void}
+ */
+ function forceTrailingCommaIfMultiline(node) {
+ if (isMultiline(node)) {
+ forceTrailingComma(node);
+ } else {
+ forbidTrailingComma(node);
+ }
+ }
+
+ /**
+ * Only if a given node is not multiline, reports the last element of a given node
+ * when it does not have a trailing comma.
+ * Otherwise, reports a trailing comma if it exists.
+ *
+ * @param {ASTNode} node - A node to check. Its type is one of
+ * ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
+ * ImportDeclaration, and ExportNamedDeclaration.
+ * @returns {void}
+ */
+ function allowTrailingCommaIfMultiline(node) {
+ if (!isMultiline(node)) {
+ forbidTrailingComma(node);
+ }
+ }
+
+ const predicate = {
+ always: forceTrailingComma,
+ "always-multiline": forceTrailingCommaIfMultiline,
+ "only-multiline": allowTrailingCommaIfMultiline,
+ never: forbidTrailingComma,
+ ignore: lodash.noop
+ };
+
+ return {
+ ObjectExpression: predicate[options.objects],
+ ObjectPattern: predicate[options.objects],
+
+ ArrayExpression: predicate[options.arrays],
+ ArrayPattern: predicate[options.arrays],
+
+ ImportDeclaration: predicate[options.imports],
+
+ ExportNamedDeclaration: predicate[options.exports],
+
+ FunctionDeclaration: predicate[options.functions],
+ FunctionExpression: predicate[options.functions],
+ ArrowFunctionExpression: predicate[options.functions],
+ CallExpression: predicate[options.functions],
+ NewExpression: predicate[options.functions]
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/comma-spacing.js b/tools/node_modules/eslint/lib/rules/comma-spacing.js
new file mode 100644
index 0000000000..25a0e7d82c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/comma-spacing.js
@@ -0,0 +1,183 @@
+/**
+ * @fileoverview Comma spacing - validates spacing before and after comma
+ * @author Vignesh Anand aka vegetableman.
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before and after commas",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ before: {
+ type: "boolean"
+ },
+ after: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode();
+ const tokensAndComments = sourceCode.tokensAndComments;
+
+ const options = {
+ before: context.options[0] ? !!context.options[0].before : false,
+ after: context.options[0] ? !!context.options[0].after : true
+ };
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ // list of comma tokens to ignore for the check of leading whitespace
+ const commaTokensToIgnore = [];
+
+ /**
+ * Reports a spacing error with an appropriate message.
+ * @param {ASTNode} node The binary expression node to report.
+ * @param {string} dir Is the error "before" or "after" the comma?
+ * @param {ASTNode} otherNode The node at the left or right of `node`
+ * @returns {void}
+ * @private
+ */
+ function report(node, dir, otherNode) {
+ context.report({
+ node,
+ fix(fixer) {
+ if (options[dir]) {
+ if (dir === "before") {
+ return fixer.insertTextBefore(node, " ");
+ }
+ return fixer.insertTextAfter(node, " ");
+
+ }
+ let start, end;
+ const newText = "";
+
+ if (dir === "before") {
+ start = otherNode.range[1];
+ end = node.range[0];
+ } else {
+ start = node.range[1];
+ end = otherNode.range[0];
+ }
+
+ return fixer.replaceTextRange([start, end], newText);
+
+ },
+ message: options[dir]
+ ? "A space is required {{dir}} ','."
+ : "There should be no space {{dir}} ','.",
+ data: {
+ dir
+ }
+ });
+ }
+
+ /**
+ * Validates the spacing around a comma token.
+ * @param {Object} tokens - The tokens to be validated.
+ * @param {Token} tokens.comma The token representing the comma.
+ * @param {Token} [tokens.left] The last token before the comma.
+ * @param {Token} [tokens.right] The first token after the comma.
+ * @param {Token|ASTNode} reportItem The item to use when reporting an error.
+ * @returns {void}
+ * @private
+ */
+ function validateCommaItemSpacing(tokens, reportItem) {
+ if (tokens.left && astUtils.isTokenOnSameLine(tokens.left, tokens.comma) &&
+ (options.before !== sourceCode.isSpaceBetweenTokens(tokens.left, tokens.comma))
+ ) {
+ report(reportItem, "before", tokens.left);
+ }
+
+ if (tokens.right && !options.after && tokens.right.type === "Line") {
+ return;
+ }
+
+ if (tokens.right && astUtils.isTokenOnSameLine(tokens.comma, tokens.right) &&
+ (options.after !== sourceCode.isSpaceBetweenTokens(tokens.comma, tokens.right))
+ ) {
+ report(reportItem, "after", tokens.right);
+ }
+ }
+
+ /**
+ * Adds null elements of the given ArrayExpression or ArrayPattern node to the ignore list.
+ * @param {ASTNode} node An ArrayExpression or ArrayPattern node.
+ * @returns {void}
+ */
+ function addNullElementsToIgnoreList(node) {
+ let previousToken = sourceCode.getFirstToken(node);
+
+ node.elements.forEach(element => {
+ let token;
+
+ if (element === null) {
+ token = sourceCode.getTokenAfter(previousToken);
+
+ if (astUtils.isCommaToken(token)) {
+ commaTokensToIgnore.push(token);
+ }
+ } else {
+ token = sourceCode.getTokenAfter(element);
+ }
+
+ previousToken = token;
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ "Program:exit"() {
+ tokensAndComments.forEach((token, i) => {
+
+ if (!astUtils.isCommaToken(token)) {
+ return;
+ }
+
+ if (token && token.type === "JSXText") {
+ return;
+ }
+
+ const previousToken = tokensAndComments[i - 1];
+ const nextToken = tokensAndComments[i + 1];
+
+ validateCommaItemSpacing({
+ comma: token,
+ left: astUtils.isCommaToken(previousToken) || commaTokensToIgnore.indexOf(token) > -1 ? null : previousToken,
+ right: astUtils.isCommaToken(nextToken) ? null : nextToken
+ }, token);
+ });
+ },
+ ArrayExpression: addNullElementsToIgnoreList,
+ ArrayPattern: addNullElementsToIgnoreList
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/comma-style.js b/tools/node_modules/eslint/lib/rules/comma-style.js
new file mode 100644
index 0000000000..4c65338a44
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/comma-style.js
@@ -0,0 +1,299 @@
+/**
+ * @fileoverview Comma style - enforces comma styles of two types: last and first
+ * @author Vignesh Anand aka vegetableman
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent comma style",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "code",
+ schema: [
+ {
+ enum: ["first", "last"]
+ },
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "object",
+ additionalProperties: {
+ type: "boolean"
+ }
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const style = context.options[0] || "last",
+ sourceCode = context.getSourceCode();
+ const exceptions = {
+ ArrayPattern: true,
+ ArrowFunctionExpression: true,
+ CallExpression: true,
+ FunctionDeclaration: true,
+ FunctionExpression: true,
+ ImportDeclaration: true,
+ ObjectPattern: true
+ };
+
+ if (context.options.length === 2 && context.options[1].hasOwnProperty("exceptions")) {
+ const keys = Object.keys(context.options[1].exceptions);
+
+ for (let i = 0; i < keys.length; i++) {
+ exceptions[keys[i]] = context.options[1].exceptions[keys[i]];
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Modified text based on the style
+ * @param {string} styleType Style type
+ * @param {string} text Source code text
+ * @returns {string} modified text
+ * @private
+ */
+ function getReplacedText(styleType, text) {
+ switch (styleType) {
+ case "between":
+ return `,${text.replace("\n", "")}`;
+
+ case "first":
+ return `${text},`;
+
+ case "last":
+ return `,${text}`;
+
+ default:
+ return "";
+ }
+ }
+
+ /**
+ * Determines the fixer function for a given style.
+ * @param {string} styleType comma style
+ * @param {ASTNode} previousItemToken The token to check.
+ * @param {ASTNode} commaToken The token to check.
+ * @param {ASTNode} currentItemToken The token to check.
+ * @returns {Function} Fixer function
+ * @private
+ */
+ function getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken) {
+ const text =
+ sourceCode.text.slice(previousItemToken.range[1], commaToken.range[0]) +
+ sourceCode.text.slice(commaToken.range[1], currentItemToken.range[0]);
+ const range = [previousItemToken.range[1], currentItemToken.range[0]];
+
+ return function(fixer) {
+ return fixer.replaceTextRange(range, getReplacedText(styleType, text));
+ };
+ }
+
+ /**
+ * Validates the spacing around single items in lists.
+ * @param {Token} previousItemToken The last token from the previous item.
+ * @param {Token} commaToken The token representing the comma.
+ * @param {Token} currentItemToken The first token of the current item.
+ * @param {Token} reportItem The item to use when reporting an error.
+ * @returns {void}
+ * @private
+ */
+ function validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem) {
+
+ // if single line
+ if (astUtils.isTokenOnSameLine(commaToken, currentItemToken) &&
+ astUtils.isTokenOnSameLine(previousItemToken, commaToken)) {
+
+ // do nothing.
+
+ } else if (!astUtils.isTokenOnSameLine(commaToken, currentItemToken) &&
+ !astUtils.isTokenOnSameLine(previousItemToken, commaToken)) {
+
+ // lone comma
+ context.report({
+ node: reportItem,
+ loc: {
+ line: commaToken.loc.end.line,
+ column: commaToken.loc.start.column
+ },
+ message: "Bad line breaking before and after ','.",
+ fix: getFixerFunction("between", previousItemToken, commaToken, currentItemToken)
+ });
+
+ } else if (style === "first" && !astUtils.isTokenOnSameLine(commaToken, currentItemToken)) {
+
+ context.report({
+ node: reportItem,
+ message: "',' should be placed first.",
+ fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
+ });
+
+ } else if (style === "last" && astUtils.isTokenOnSameLine(commaToken, currentItemToken)) {
+
+ context.report({
+ node: reportItem,
+ loc: {
+ line: commaToken.loc.end.line,
+ column: commaToken.loc.end.column
+ },
+ message: "',' should be placed last.",
+ fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
+ });
+ }
+ }
+
+ /**
+ * Checks the comma placement with regards to a declaration/property/element
+ * @param {ASTNode} node The binary expression node to check
+ * @param {string} property The property of the node containing child nodes.
+ * @private
+ * @returns {void}
+ */
+ function validateComma(node, property) {
+ const items = node[property],
+ arrayLiteral = (node.type === "ArrayExpression" || node.type === "ArrayPattern");
+
+ if (items.length > 1 || arrayLiteral) {
+
+ // seed as opening [
+ let previousItemToken = sourceCode.getFirstToken(node);
+
+ items.forEach(item => {
+ const commaToken = item ? sourceCode.getTokenBefore(item) : previousItemToken,
+ currentItemToken = item ? sourceCode.getFirstToken(item) : sourceCode.getTokenAfter(commaToken),
+ reportItem = item || currentItemToken,
+ tokenBeforeComma = sourceCode.getTokenBefore(commaToken);
+
+ // Check if previous token is wrapped in parentheses
+ if (tokenBeforeComma && astUtils.isClosingParenToken(tokenBeforeComma)) {
+ previousItemToken = tokenBeforeComma;
+ }
+
+ /*
+ * This works by comparing three token locations:
+ * - previousItemToken is the last token of the previous item
+ * - commaToken is the location of the comma before the current item
+ * - currentItemToken is the first token of the current item
+ *
+ * These values get switched around if item is undefined.
+ * previousItemToken will refer to the last token not belonging
+ * to the current item, which could be a comma or an opening
+ * square bracket. currentItemToken could be a comma.
+ *
+ * All comparisons are done based on these tokens directly, so
+ * they are always valid regardless of an undefined item.
+ */
+ if (astUtils.isCommaToken(commaToken)) {
+ validateCommaItemSpacing(previousItemToken, commaToken,
+ currentItemToken, reportItem);
+ }
+
+ if (item) {
+ const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken);
+
+ previousItemToken = tokenAfterItem
+ ? sourceCode.getTokenBefore(tokenAfterItem)
+ : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
+ }
+ });
+
+ /*
+ * Special case for array literals that have empty last items, such
+ * as [ 1, 2, ]. These arrays only have two items show up in the
+ * AST, so we need to look at the token to verify that there's no
+ * dangling comma.
+ */
+ if (arrayLiteral) {
+
+ const lastToken = sourceCode.getLastToken(node),
+ nextToLastToken = sourceCode.getTokenBefore(lastToken);
+
+ if (astUtils.isCommaToken(nextToLastToken)) {
+ validateCommaItemSpacing(
+ sourceCode.getTokenBefore(nextToLastToken),
+ nextToLastToken,
+ lastToken,
+ lastToken
+ );
+ }
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ const nodes = {};
+
+ if (!exceptions.VariableDeclaration) {
+ nodes.VariableDeclaration = function(node) {
+ validateComma(node, "declarations");
+ };
+ }
+ if (!exceptions.ObjectExpression) {
+ nodes.ObjectExpression = function(node) {
+ validateComma(node, "properties");
+ };
+ }
+ if (!exceptions.ObjectPattern) {
+ nodes.ObjectPattern = function(node) {
+ validateComma(node, "properties");
+ };
+ }
+ if (!exceptions.ArrayExpression) {
+ nodes.ArrayExpression = function(node) {
+ validateComma(node, "elements");
+ };
+ }
+ if (!exceptions.ArrayPattern) {
+ nodes.ArrayPattern = function(node) {
+ validateComma(node, "elements");
+ };
+ }
+ if (!exceptions.FunctionDeclaration) {
+ nodes.FunctionDeclaration = function(node) {
+ validateComma(node, "params");
+ };
+ }
+ if (!exceptions.FunctionExpression) {
+ nodes.FunctionExpression = function(node) {
+ validateComma(node, "params");
+ };
+ }
+ if (!exceptions.ArrowFunctionExpression) {
+ nodes.ArrowFunctionExpression = function(node) {
+ validateComma(node, "params");
+ };
+ }
+ if (!exceptions.CallExpression) {
+ nodes.CallExpression = function(node) {
+ validateComma(node, "arguments");
+ };
+ }
+ if (!exceptions.ImportDeclaration) {
+ nodes.ImportDeclaration = function(node) {
+ validateComma(node, "specifiers");
+ };
+ }
+
+ return nodes;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/complexity.js b/tools/node_modules/eslint/lib/rules/complexity.js
new file mode 100644
index 0000000000..e0313fa78f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/complexity.js
@@ -0,0 +1,168 @@
+/**
+ * @fileoverview Counts the cyclomatic complexity of each function of the script. See http://en.wikipedia.org/wiki/Cyclomatic_complexity.
+ * Counts the number of if, conditional, for, whilte, try, switch/case,
+ * @author Patrick Brosset
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum cyclomatic complexity allowed in a program",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ maximum: {
+ type: "integer",
+ minimum: 0
+ },
+ max: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const option = context.options[0];
+ let THRESHOLD = 20;
+
+ if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
+ THRESHOLD = option.maximum;
+ }
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
+ THRESHOLD = option.max;
+ }
+ if (typeof option === "number") {
+ THRESHOLD = option;
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ // Using a stack to store complexity (handling nested functions)
+ const fns = [];
+
+ /**
+ * When parsing a new function, store it in our function stack
+ * @returns {void}
+ * @private
+ */
+ function startFunction() {
+ fns.push(1);
+ }
+
+ /**
+ * Evaluate the node at the end of function
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function endFunction(node) {
+ const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
+ const complexity = fns.pop();
+
+ if (complexity > THRESHOLD) {
+ context.report({
+ node,
+ message: "{{name}} has a complexity of {{complexity}}.",
+ data: { name, complexity }
+ });
+ }
+ }
+
+ /**
+ * Increase the complexity of the function in context
+ * @returns {void}
+ * @private
+ */
+ function increaseComplexity() {
+ if (fns.length) {
+ fns[fns.length - 1]++;
+ }
+ }
+
+ /**
+ * Increase the switch complexity in context
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function increaseSwitchComplexity(node) {
+
+ // Avoiding `default`
+ if (node.test) {
+ increaseComplexity();
+ }
+ }
+
+ /**
+ * Increase the logical path complexity in context
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function increaseLogicalComplexity(node) {
+
+ // Avoiding &&
+ if (node.operator === "||") {
+ increaseComplexity();
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ FunctionDeclaration: startFunction,
+ FunctionExpression: startFunction,
+ ArrowFunctionExpression: startFunction,
+ "FunctionDeclaration:exit": endFunction,
+ "FunctionExpression:exit": endFunction,
+ "ArrowFunctionExpression:exit": endFunction,
+
+ CatchClause: increaseComplexity,
+ ConditionalExpression: increaseComplexity,
+ LogicalExpression: increaseLogicalComplexity,
+ ForStatement: increaseComplexity,
+ ForInStatement: increaseComplexity,
+ ForOfStatement: increaseComplexity,
+ IfStatement: increaseComplexity,
+ SwitchCase: increaseSwitchComplexity,
+ WhileStatement: increaseComplexity,
+ DoWhileStatement: increaseComplexity
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js
new file mode 100644
index 0000000000..19c28fd22e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js
@@ -0,0 +1,176 @@
+/**
+ * @fileoverview Disallows or enforces spaces inside computed properties.
+ * @author Jamund Ferguson
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing inside computed property brackets",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports that there shouldn't be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @param {Token} tokenAfter - The token after `token`.
+ * @returns {void}
+ */
+ function reportNoBeginningSpace(node, token, tokenAfter) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "There should be no space after '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @param {Token} tokenBefore - The token before `token`.
+ * @returns {void}
+ */
+ function reportNoEndingSpace(node, token, tokenBefore) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "There should be no space before '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredBeginningSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "A space is required after '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredEndingSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "A space is required before '{{tokenValue}}'.",
+ data: {
+ tokenValue: token.value
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ });
+ }
+
+ /**
+ * Returns a function that checks the spacing of a node on the property name
+ * that was passed in.
+ * @param {string} propertyName The property on the node to check for spacing
+ * @returns {Function} A function that will check spacing on a node
+ */
+ function checkSpacing(propertyName) {
+ return function(node) {
+ if (!node.computed) {
+ return;
+ }
+
+ const property = node[propertyName];
+
+ const before = sourceCode.getTokenBefore(property),
+ first = sourceCode.getFirstToken(property),
+ last = sourceCode.getLastToken(property),
+ after = sourceCode.getTokenAfter(property);
+
+ if (astUtils.isTokenOnSameLine(before, first)) {
+ if (propertyNameMustBeSpaced) {
+ if (!sourceCode.isSpaceBetweenTokens(before, first) && astUtils.isTokenOnSameLine(before, first)) {
+ reportRequiredBeginningSpace(node, before);
+ }
+ } else {
+ if (sourceCode.isSpaceBetweenTokens(before, first)) {
+ reportNoBeginningSpace(node, before, first);
+ }
+ }
+ }
+
+ if (astUtils.isTokenOnSameLine(last, after)) {
+ if (propertyNameMustBeSpaced) {
+ if (!sourceCode.isSpaceBetweenTokens(last, after) && astUtils.isTokenOnSameLine(last, after)) {
+ reportRequiredEndingSpace(node, after);
+ }
+ } else {
+ if (sourceCode.isSpaceBetweenTokens(last, after)) {
+ reportNoEndingSpace(node, after, last);
+ }
+ }
+ }
+ };
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Property: checkSpacing("key"),
+ MemberExpression: checkSpacing("property")
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/consistent-return.js b/tools/node_modules/eslint/lib/rules/consistent-return.js
new file mode 100644
index 0000000000..a42faaa1ed
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/consistent-return.js
@@ -0,0 +1,188 @@
+/**
+ * @fileoverview Rule to flag consistent return values
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is an `Identifier` node which was named a given name.
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - An expected name of the node.
+ * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
+ */
+function isIdentifier(node, name) {
+ return node.type === "Identifier" && node.name === name;
+}
+
+/**
+ * Checks whether or not a given code path segment is unreachable.
+ * @param {CodePathSegment} segment - A CodePathSegment to check.
+ * @returns {boolean} `true` if the segment is unreachable.
+ */
+function isUnreachable(segment) {
+ return !segment.reachable;
+}
+
+/**
+ * Checks whether a given node is a `constructor` method in an ES6 class
+ * @param {ASTNode} node A node to check
+ * @returns {boolean} `true` if the node is a `constructor` method
+ */
+function isClassConstructor(node) {
+ return node.type === "FunctionExpression" &&
+ node.parent &&
+ node.parent.type === "MethodDefinition" &&
+ node.parent.kind === "constructor";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `return` statements to either always or never specify values",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [{
+ type: "object",
+ properties: {
+ treatUndefinedAsUnspecified: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const treatUndefinedAsUnspecified = options.treatUndefinedAsUnspecified === true;
+ let funcInfo = null;
+
+ /**
+ * Checks whether of not the implicit returning is consistent if the last
+ * code path segment is reachable.
+ *
+ * @param {ASTNode} node - A program/function node to check.
+ * @returns {void}
+ */
+ function checkLastSegment(node) {
+ let loc, name;
+
+ /*
+ * Skip if it expected no return value or unreachable.
+ * When unreachable, all paths are returned or thrown.
+ */
+ if (!funcInfo.hasReturnValue ||
+ funcInfo.codePath.currentSegments.every(isUnreachable) ||
+ astUtils.isES5Constructor(node) ||
+ isClassConstructor(node)
+ ) {
+ return;
+ }
+
+ // Adjust a location and a message.
+ if (node.type === "Program") {
+
+ // The head of program.
+ loc = { line: 1, column: 0 };
+ name = "program";
+ } else if (node.type === "ArrowFunctionExpression") {
+
+ // `=>` token
+ loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
+ } else if (
+ node.parent.type === "MethodDefinition" ||
+ (node.parent.type === "Property" && node.parent.method)
+ ) {
+
+ // Method name.
+ loc = node.parent.key.loc.start;
+ } else {
+
+ // Function name or `function` keyword.
+ loc = (node.id || node).loc.start;
+ }
+
+ if (!name) {
+ name = astUtils.getFunctionNameWithKind(node);
+ }
+
+ // Reports.
+ context.report({
+ node,
+ loc,
+ message: "Expected to return a value at the end of {{name}}.",
+ data: { name }
+ });
+ }
+
+ return {
+
+ // Initializes/Disposes state of each code path.
+ onCodePathStart(codePath, node) {
+ funcInfo = {
+ upper: funcInfo,
+ codePath,
+ hasReturn: false,
+ hasReturnValue: false,
+ message: "",
+ node
+ };
+ },
+ onCodePathEnd() {
+ funcInfo = funcInfo.upper;
+ },
+
+ // Reports a given return statement if it's inconsistent.
+ ReturnStatement(node) {
+ const argument = node.argument;
+ let hasReturnValue = Boolean(argument);
+
+ if (treatUndefinedAsUnspecified && hasReturnValue) {
+ hasReturnValue = !isIdentifier(argument, "undefined") && argument.operator !== "void";
+ }
+
+ if (!funcInfo.hasReturn) {
+ funcInfo.hasReturn = true;
+ funcInfo.hasReturnValue = hasReturnValue;
+ funcInfo.message = "{{name}} expected {{which}} return value.";
+ funcInfo.data = {
+ name: funcInfo.node.type === "Program"
+ ? "Program"
+ : lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node)),
+ which: hasReturnValue ? "a" : "no"
+ };
+ } else if (funcInfo.hasReturnValue !== hasReturnValue) {
+ context.report({
+ node,
+ message: funcInfo.message,
+ data: funcInfo.data
+ });
+ }
+ },
+
+ // Reports a given program/function if the implicit returning is not consistent.
+ "Program:exit": checkLastSegment,
+ "FunctionDeclaration:exit": checkLastSegment,
+ "FunctionExpression:exit": checkLastSegment,
+ "ArrowFunctionExpression:exit": checkLastSegment
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/consistent-this.js b/tools/node_modules/eslint/lib/rules/consistent-this.js
new file mode 100644
index 0000000000..151cdcf9c9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/consistent-this.js
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview Rule to enforce consistent naming of "this" context variables
+ * @author Raphael Pigulla
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent naming when capturing the current execution context",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: {
+ type: "array",
+ items: {
+ type: "string",
+ minLength: 1
+ },
+ uniqueItems: true
+ }
+ },
+
+ create(context) {
+ let aliases = [];
+
+ if (context.options.length === 0) {
+ aliases.push("that");
+ } else {
+ aliases = context.options;
+ }
+
+ /**
+ * Reports that a variable declarator or assignment expression is assigning
+ * a non-'this' value to the specified alias.
+ * @param {ASTNode} node - The assigning node.
+ * @param {string} alias - the name of the alias that was incorrectly used.
+ * @returns {void}
+ */
+ function reportBadAssignment(node, alias) {
+ context.report({ node, message: "Designated alias '{{alias}}' is not assigned to 'this'.", data: { alias } });
+ }
+
+ /**
+ * Checks that an assignment to an identifier only assigns 'this' to the
+ * appropriate alias, and the alias is only assigned to 'this'.
+ * @param {ASTNode} node - The assigning node.
+ * @param {Identifier} name - The name of the variable assigned to.
+ * @param {Expression} value - The value of the assignment.
+ * @returns {void}
+ */
+ function checkAssignment(node, name, value) {
+ const isThis = value.type === "ThisExpression";
+
+ if (aliases.indexOf(name) !== -1) {
+ if (!isThis || node.operator && node.operator !== "=") {
+ reportBadAssignment(node, name);
+ }
+ } else if (isThis) {
+ context.report({ node, message: "Unexpected alias '{{name}}' for 'this'.", data: { name } });
+ }
+ }
+
+ /**
+ * Ensures that a variable declaration of the alias in a program or function
+ * is assigned to the correct value.
+ * @param {string} alias alias the check the assignment of.
+ * @param {Object} scope scope of the current code we are checking.
+ * @private
+ * @returns {void}
+ */
+ function checkWasAssigned(alias, scope) {
+ const variable = scope.set.get(alias);
+
+ if (!variable) {
+ return;
+ }
+
+ if (variable.defs.some(def => def.node.type === "VariableDeclarator" &&
+ def.node.init !== null)) {
+ return;
+ }
+
+ /*
+ * The alias has been declared and not assigned: check it was
+ * assigned later in the same scope.
+ */
+ if (!variable.references.some(reference => {
+ const write = reference.writeExpr;
+
+ return (
+ reference.from === scope &&
+ write && write.type === "ThisExpression" &&
+ write.parent.operator === "="
+ );
+ })) {
+ variable.defs.map(def => def.node).forEach(node => {
+ reportBadAssignment(node, alias);
+ });
+ }
+ }
+
+ /**
+ * Check each alias to ensure that is was assinged to the correct value.
+ * @returns {void}
+ */
+ function ensureWasAssigned() {
+ const scope = context.getScope();
+
+ aliases.forEach(alias => {
+ checkWasAssigned(alias, scope);
+ });
+ }
+
+ return {
+ "Program:exit": ensureWasAssigned,
+ "FunctionExpression:exit": ensureWasAssigned,
+ "FunctionDeclaration:exit": ensureWasAssigned,
+
+ VariableDeclarator(node) {
+ const id = node.id;
+ const isDestructuring =
+ id.type === "ArrayPattern" || id.type === "ObjectPattern";
+
+ if (node.init !== null && !isDestructuring) {
+ checkAssignment(node, id.name, node.init);
+ }
+ },
+
+ AssignmentExpression(node) {
+ if (node.left.type === "Identifier") {
+ checkAssignment(node, node.left.name, node.right);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/constructor-super.js b/tools/node_modules/eslint/lib/rules/constructor-super.js
new file mode 100644
index 0000000000..d0a238df8e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/constructor-super.js
@@ -0,0 +1,385 @@
+/**
+ * @fileoverview A rule to verify `super()` callings in constructor.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether a given code path segment is reachable or not.
+ *
+ * @param {CodePathSegment} segment - A code path segment to check.
+ * @returns {boolean} `true` if the segment is reachable.
+ */
+function isReachable(segment) {
+ return segment.reachable;
+}
+
+/**
+ * Checks whether or not a given node is a constructor.
+ * @param {ASTNode} node - A node to check. This node type is one of
+ * `Program`, `FunctionDeclaration`, `FunctionExpression`, and
+ * `ArrowFunctionExpression`.
+ * @returns {boolean} `true` if the node is a constructor.
+ */
+function isConstructorFunction(node) {
+ return (
+ node.type === "FunctionExpression" &&
+ node.parent.type === "MethodDefinition" &&
+ node.parent.kind === "constructor"
+ );
+}
+
+/**
+ * Checks whether a given node can be a constructor or not.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node can be a constructor.
+ */
+function isPossibleConstructor(node) {
+ if (!node) {
+ return false;
+ }
+
+ switch (node.type) {
+ case "ClassExpression":
+ case "FunctionExpression":
+ case "ThisExpression":
+ case "MemberExpression":
+ case "CallExpression":
+ case "NewExpression":
+ case "YieldExpression":
+ case "TaggedTemplateExpression":
+ case "MetaProperty":
+ return true;
+
+ case "Identifier":
+ return node.name !== "undefined";
+
+ case "AssignmentExpression":
+ return isPossibleConstructor(node.right);
+
+ case "LogicalExpression":
+ return (
+ isPossibleConstructor(node.left) ||
+ isPossibleConstructor(node.right)
+ );
+
+ case "ConditionalExpression":
+ return (
+ isPossibleConstructor(node.alternate) ||
+ isPossibleConstructor(node.consequent)
+ );
+
+ case "SequenceExpression": {
+ const lastExpression = node.expressions[node.expressions.length - 1];
+
+ return isPossibleConstructor(lastExpression);
+ }
+
+ default:
+ return false;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `super()` calls in constructors",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /*
+ * {{hasExtends: boolean, scope: Scope, codePath: CodePath}[]}
+ * Information for each constructor.
+ * - upper: Information of the upper constructor.
+ * - hasExtends: A flag which shows whether own class has a valid `extends`
+ * part.
+ * - scope: The scope of own class.
+ * - codePath: The code path object of the constructor.
+ */
+ let funcInfo = null;
+
+ /*
+ * {Map<string, {calledInSomePaths: boolean, calledInEveryPaths: boolean}>}
+ * Information for each code path segment.
+ * - calledInSomePaths: A flag of be called `super()` in some code paths.
+ * - calledInEveryPaths: A flag of be called `super()` in all code paths.
+ * - validNodes:
+ */
+ let segInfoMap = Object.create(null);
+
+ /**
+ * Gets the flag which shows `super()` is called in some paths.
+ * @param {CodePathSegment} segment - A code path segment to get.
+ * @returns {boolean} The flag which shows `super()` is called in some paths
+ */
+ function isCalledInSomePath(segment) {
+ return segment.reachable && segInfoMap[segment.id].calledInSomePaths;
+ }
+
+ /**
+ * Gets the flag which shows `super()` is called in all paths.
+ * @param {CodePathSegment} segment - A code path segment to get.
+ * @returns {boolean} The flag which shows `super()` is called in all paths.
+ */
+ function isCalledInEveryPath(segment) {
+
+ /*
+ * If specific segment is the looped segment of the current segment,
+ * skip the segment.
+ * If not skipped, this never becomes true after a loop.
+ */
+ if (segment.nextSegments.length === 1 &&
+ segment.nextSegments[0].isLoopedPrevSegment(segment)
+ ) {
+ return true;
+ }
+ return segment.reachable && segInfoMap[segment.id].calledInEveryPaths;
+ }
+
+ return {
+
+ /**
+ * Stacks a constructor information.
+ * @param {CodePath} codePath - A code path which was started.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathStart(codePath, node) {
+ if (isConstructorFunction(node)) {
+
+ // Class > ClassBody > MethodDefinition > FunctionExpression
+ const classNode = node.parent.parent.parent;
+ const superClass = classNode.superClass;
+
+ funcInfo = {
+ upper: funcInfo,
+ isConstructor: true,
+ hasExtends: Boolean(superClass),
+ superIsConstructor: isPossibleConstructor(superClass),
+ codePath
+ };
+ } else {
+ funcInfo = {
+ upper: funcInfo,
+ isConstructor: false,
+ hasExtends: false,
+ superIsConstructor: false,
+ codePath
+ };
+ }
+ },
+
+ /**
+ * Pops a constructor information.
+ * And reports if `super()` lacked.
+ * @param {CodePath} codePath - A code path which was ended.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathEnd(codePath, node) {
+ const hasExtends = funcInfo.hasExtends;
+
+ // Pop.
+ funcInfo = funcInfo.upper;
+
+ if (!hasExtends) {
+ return;
+ }
+
+ // Reports if `super()` lacked.
+ const segments = codePath.returnedSegments;
+ const calledInEveryPaths = segments.every(isCalledInEveryPath);
+ const calledInSomePaths = segments.some(isCalledInSomePath);
+
+ if (!calledInEveryPaths) {
+ context.report({
+ message: calledInSomePaths
+ ? "Lacked a call of 'super()' in some code paths."
+ : "Expected to call 'super()'.",
+ node: node.parent
+ });
+ }
+ },
+
+ /**
+ * Initialize information of a given code path segment.
+ * @param {CodePathSegment} segment - A code path segment to initialize.
+ * @returns {void}
+ */
+ onCodePathSegmentStart(segment) {
+ if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
+ return;
+ }
+
+ // Initialize info.
+ const info = segInfoMap[segment.id] = {
+ calledInSomePaths: false,
+ calledInEveryPaths: false,
+ validNodes: []
+ };
+
+ // When there are previous segments, aggregates these.
+ const prevSegments = segment.prevSegments;
+
+ if (prevSegments.length > 0) {
+ info.calledInSomePaths = prevSegments.some(isCalledInSomePath);
+ info.calledInEveryPaths = prevSegments.every(isCalledInEveryPath);
+ }
+ },
+
+ /**
+ * Update information of the code path segment when a code path was
+ * looped.
+ * @param {CodePathSegment} fromSegment - The code path segment of the
+ * end of a loop.
+ * @param {CodePathSegment} toSegment - A code path segment of the head
+ * of a loop.
+ * @returns {void}
+ */
+ onCodePathSegmentLoop(fromSegment, toSegment) {
+ if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
+ return;
+ }
+
+ // Update information inside of the loop.
+ const isRealLoop = toSegment.prevSegments.length >= 2;
+
+ funcInfo.codePath.traverseSegments(
+ { first: toSegment, last: fromSegment },
+ segment => {
+ const info = segInfoMap[segment.id];
+ const prevSegments = segment.prevSegments;
+
+ // Updates flags.
+ info.calledInSomePaths = prevSegments.some(isCalledInSomePath);
+ info.calledInEveryPaths = prevSegments.every(isCalledInEveryPath);
+
+ // If flags become true anew, reports the valid nodes.
+ if (info.calledInSomePaths || isRealLoop) {
+ const nodes = info.validNodes;
+
+ info.validNodes = [];
+
+ for (let i = 0; i < nodes.length; ++i) {
+ const node = nodes[i];
+
+ context.report({
+ message: "Unexpected duplicate 'super()'.",
+ node
+ });
+ }
+ }
+ }
+ );
+ },
+
+ /**
+ * Checks for a call of `super()`.
+ * @param {ASTNode} node - A CallExpression node to check.
+ * @returns {void}
+ */
+ "CallExpression:exit"(node) {
+ if (!(funcInfo && funcInfo.isConstructor)) {
+ return;
+ }
+
+ // Skips except `super()`.
+ if (node.callee.type !== "Super") {
+ return;
+ }
+
+ // Reports if needed.
+ if (funcInfo.hasExtends) {
+ const segments = funcInfo.codePath.currentSegments;
+ let duplicate = false;
+ let info = null;
+
+ for (let i = 0; i < segments.length; ++i) {
+ const segment = segments[i];
+
+ if (segment.reachable) {
+ info = segInfoMap[segment.id];
+
+ duplicate = duplicate || info.calledInSomePaths;
+ info.calledInSomePaths = info.calledInEveryPaths = true;
+ }
+ }
+
+ if (info) {
+ if (duplicate) {
+ context.report({
+ message: "Unexpected duplicate 'super()'.",
+ node
+ });
+ } else if (!funcInfo.superIsConstructor) {
+ context.report({
+ message: "Unexpected 'super()' because 'super' is not a constructor.",
+ node
+ });
+ } else {
+ info.validNodes.push(node);
+ }
+ }
+ } else if (funcInfo.codePath.currentSegments.some(isReachable)) {
+ context.report({
+ message: "Unexpected 'super()'.",
+ node
+ });
+ }
+ },
+
+ /**
+ * Set the mark to the returned path as `super()` was called.
+ * @param {ASTNode} node - A ReturnStatement node to check.
+ * @returns {void}
+ */
+ ReturnStatement(node) {
+ if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
+ return;
+ }
+
+ // Skips if no argument.
+ if (!node.argument) {
+ return;
+ }
+
+ // Returning argument is a substitute of 'super()'.
+ const segments = funcInfo.codePath.currentSegments;
+
+ for (let i = 0; i < segments.length; ++i) {
+ const segment = segments[i];
+
+ if (segment.reachable) {
+ const info = segInfoMap[segment.id];
+
+ info.calledInSomePaths = info.calledInEveryPaths = true;
+ }
+ }
+ },
+
+ /**
+ * Resets state.
+ * @returns {void}
+ */
+ "Program:exit"() {
+ segInfoMap = Object.create(null);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/curly.js b/tools/node_modules/eslint/lib/rules/curly.js
new file mode 100644
index 0000000000..37f07d95ee
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/curly.js
@@ -0,0 +1,399 @@
+/**
+ * @fileoverview Rule to flag statements without curly braces
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent brace style for all control statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["all"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["multi", "multi-line", "multi-or-nest"]
+ },
+ {
+ enum: ["consistent"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ },
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const multiOnly = (context.options[0] === "multi");
+ const multiLine = (context.options[0] === "multi-line");
+ const multiOrNest = (context.options[0] === "multi-or-nest");
+ const consistent = (context.options[1] === "consistent");
+
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Determines if a given node is a one-liner that's on the same line as it's preceding code.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} True if the node is a one-liner that's on the same line as it's preceding code.
+ * @private
+ */
+ function isCollapsedOneLiner(node) {
+ const before = sourceCode.getTokenBefore(node);
+ const last = sourceCode.getLastToken(node);
+ const lastExcludingSemicolon = astUtils.isSemicolonToken(last) ? sourceCode.getTokenBefore(last) : last;
+
+ return before.loc.start.line === lastExcludingSemicolon.loc.end.line;
+ }
+
+ /**
+ * Determines if a given node is a one-liner.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} True if the node is a one-liner.
+ * @private
+ */
+ function isOneLiner(node) {
+ const first = sourceCode.getFirstToken(node),
+ last = sourceCode.getLastToken(node);
+
+ return first.loc.start.line === last.loc.end.line;
+ }
+
+ /**
+ * Checks if the given token is an `else` token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is an `else` token.
+ */
+ function isElseKeywordToken(token) {
+ return token.value === "else" && token.type === "Keyword";
+ }
+
+ /**
+ * Gets the `else` keyword token of a given `IfStatement` node.
+ * @param {ASTNode} node - A `IfStatement` node to get.
+ * @returns {Token} The `else` keyword token.
+ */
+ function getElseKeyword(node) {
+ return node.alternate && sourceCode.getFirstTokenBetween(node.consequent, node.alternate, isElseKeywordToken);
+ }
+
+ /**
+ * Checks a given IfStatement node requires braces of the consequent chunk.
+ * This returns `true` when below:
+ *
+ * 1. The given node has the `alternate` node.
+ * 2. There is a `IfStatement` which doesn't have `alternate` node in the
+ * trailing statement chain of the `consequent` node.
+ *
+ * @param {ASTNode} node - A IfStatement node to check.
+ * @returns {boolean} `true` if the node requires braces of the consequent chunk.
+ */
+ function requiresBraceOfConsequent(node) {
+ if (node.alternate && node.consequent.type === "BlockStatement") {
+ if (node.consequent.body.length >= 2) {
+ return true;
+ }
+
+ node = node.consequent.body[0];
+ while (node) {
+ if (node.type === "IfStatement" && !node.alternate) {
+ return true;
+ }
+ node = astUtils.getTrailingStatement(node);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Reports "Expected { after ..." error
+ * @param {ASTNode} node The node to report.
+ * @param {ASTNode} bodyNode The body node that is incorrectly missing curly brackets
+ * @param {string} name The name to report.
+ * @param {string} suffix Additional string to add to the end of a report.
+ * @returns {void}
+ * @private
+ */
+ function reportExpectedBraceError(node, bodyNode, name, suffix) {
+ context.report({
+ node,
+ loc: (name !== "else" ? node : getElseKeyword(node)).loc.start,
+ message: "Expected { after '{{name}}'{{suffix}}.",
+ data: {
+ name,
+ suffix: (suffix ? ` ${suffix}` : "")
+ },
+ fix: fixer => fixer.replaceText(bodyNode, `{${sourceCode.getText(bodyNode)}}`)
+ });
+ }
+
+ /**
+ * Determines if a semicolon needs to be inserted after removing a set of curly brackets, in order to avoid a SyntaxError.
+ * @param {Token} closingBracket The } token
+ * @returns {boolean} `true` if a semicolon needs to be inserted after the last statement in the block.
+ */
+ function needsSemicolon(closingBracket) {
+ const tokenBefore = sourceCode.getTokenBefore(closingBracket);
+ const tokenAfter = sourceCode.getTokenAfter(closingBracket);
+ const lastBlockNode = sourceCode.getNodeByRangeIndex(tokenBefore.range[0]);
+
+ if (astUtils.isSemicolonToken(tokenBefore)) {
+
+ // If the last statement already has a semicolon, don't add another one.
+ return false;
+ }
+
+ if (!tokenAfter) {
+
+ // If there are no statements after this block, there is no need to add a semicolon.
+ return false;
+ }
+
+ if (lastBlockNode.type === "BlockStatement" && lastBlockNode.parent.type !== "FunctionExpression" && lastBlockNode.parent.type !== "ArrowFunctionExpression") {
+
+ /*
+ * If the last node surrounded by curly brackets is a BlockStatement (other than a FunctionExpression or an ArrowFunctionExpression),
+ * don't insert a semicolon. Otherwise, the semicolon would be parsed as a separate statement, which would cause
+ * a SyntaxError if it was followed by `else`.
+ */
+ return false;
+ }
+
+ if (tokenBefore.loc.end.line === tokenAfter.loc.start.line) {
+
+ // If the next token is on the same line, insert a semicolon.
+ return true;
+ }
+
+ if (/^[([/`+-]/.test(tokenAfter.value)) {
+
+ // If the next token starts with a character that would disrupt ASI, insert a semicolon.
+ return true;
+ }
+
+ if (tokenBefore.type === "Punctuator" && (tokenBefore.value === "++" || tokenBefore.value === "--")) {
+
+ // If the last token is ++ or --, insert a semicolon to avoid disrupting ASI.
+ return true;
+ }
+
+ // Otherwise, do not insert a semicolon.
+ return false;
+ }
+
+ /**
+ * Reports "Unnecessary { after ..." error
+ * @param {ASTNode} node The node to report.
+ * @param {ASTNode} bodyNode The block statement that is incorrectly surrounded by parens
+ * @param {string} name The name to report.
+ * @param {string} suffix Additional string to add to the end of a report.
+ * @returns {void}
+ * @private
+ */
+ function reportUnnecessaryBraceError(node, bodyNode, name, suffix) {
+ context.report({
+ node,
+ loc: (name !== "else" ? node : getElseKeyword(node)).loc.start,
+ message: "Unnecessary { after '{{name}}'{{suffix}}.",
+ data: {
+ name,
+ suffix: (suffix ? ` ${suffix}` : "")
+ },
+ fix(fixer) {
+
+ /*
+ * `do while` expressions sometimes need a space to be inserted after `do`.
+ * e.g. `do{foo()} while (bar)` should be corrected to `do foo() while (bar)`
+ */
+ const needsPrecedingSpace = node.type === "DoWhileStatement" &&
+ sourceCode.getTokenBefore(bodyNode).range[1] === bodyNode.range[0] &&
+ !astUtils.canTokensBeAdjacent("do", sourceCode.getFirstToken(bodyNode, { skip: 1 }));
+
+ const openingBracket = sourceCode.getFirstToken(bodyNode);
+ const closingBracket = sourceCode.getLastToken(bodyNode);
+ const lastTokenInBlock = sourceCode.getTokenBefore(closingBracket);
+
+ if (needsSemicolon(closingBracket)) {
+
+ /*
+ * If removing braces would cause a SyntaxError due to multiple statements on the same line (or
+ * change the semantics of the code due to ASI), don't perform a fix.
+ */
+ return null;
+ }
+
+ const resultingBodyText = sourceCode.getText().slice(openingBracket.range[1], lastTokenInBlock.range[0]) +
+ sourceCode.getText(lastTokenInBlock) +
+ sourceCode.getText().slice(lastTokenInBlock.range[1], closingBracket.range[0]);
+
+ return fixer.replaceText(bodyNode, (needsPrecedingSpace ? " " : "") + resultingBodyText);
+ }
+ });
+ }
+
+ /**
+ * Prepares to check the body of a node to see if it's a block statement.
+ * @param {ASTNode} node The node to report if there's a problem.
+ * @param {ASTNode} body The body node to check for blocks.
+ * @param {string} name The name to report if there's a problem.
+ * @param {string} suffix Additional string to add to the end of a report.
+ * @returns {Object} a prepared check object, with "actual", "expected", "check" properties.
+ * "actual" will be `true` or `false` whether the body is already a block statement.
+ * "expected" will be `true` or `false` if the body should be a block statement or not, or
+ * `null` if it doesn't matter, depending on the rule options. It can be modified to change
+ * the final behavior of "check".
+ * "check" will be a function reporting appropriate problems depending on the other
+ * properties.
+ */
+ function prepareCheck(node, body, name, suffix) {
+ const hasBlock = (body.type === "BlockStatement");
+ let expected = null;
+
+ if (node.type === "IfStatement" && node.consequent === body && requiresBraceOfConsequent(node)) {
+ expected = true;
+ } else if (multiOnly) {
+ if (hasBlock && body.body.length === 1) {
+ expected = false;
+ }
+ } else if (multiLine) {
+ if (!isCollapsedOneLiner(body)) {
+ expected = true;
+ }
+ } else if (multiOrNest) {
+ if (hasBlock && body.body.length === 1 && isOneLiner(body.body[0])) {
+ const leadingComments = sourceCode.getCommentsBefore(body.body[0]);
+
+ expected = leadingComments.length > 0;
+ } else if (!isOneLiner(body)) {
+ expected = true;
+ }
+ } else {
+ expected = true;
+ }
+
+ return {
+ actual: hasBlock,
+ expected,
+ check() {
+ if (this.expected !== null && this.expected !== this.actual) {
+ if (this.expected) {
+ reportExpectedBraceError(node, body, name, suffix);
+ } else {
+ reportUnnecessaryBraceError(node, body, name, suffix);
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Prepares to check the bodies of a "if", "else if" and "else" chain.
+ * @param {ASTNode} node The first IfStatement node of the chain.
+ * @returns {Object[]} prepared checks for each body of the chain. See `prepareCheck` for more
+ * information.
+ */
+ function prepareIfChecks(node) {
+ const preparedChecks = [];
+
+ do {
+ preparedChecks.push(prepareCheck(node, node.consequent, "if", "condition"));
+ if (node.alternate && node.alternate.type !== "IfStatement") {
+ preparedChecks.push(prepareCheck(node, node.alternate, "else"));
+ break;
+ }
+ node = node.alternate;
+ } while (node);
+
+ if (consistent) {
+
+ /*
+ * If any node should have or already have braces, make sure they
+ * all have braces.
+ * If all nodes shouldn't have braces, make sure they don't.
+ */
+ const expected = preparedChecks.some(preparedCheck => {
+ if (preparedCheck.expected !== null) {
+ return preparedCheck.expected;
+ }
+ return preparedCheck.actual;
+ });
+
+ preparedChecks.forEach(preparedCheck => {
+ preparedCheck.expected = expected;
+ });
+ }
+
+ return preparedChecks;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ IfStatement(node) {
+ if (node.parent.type !== "IfStatement") {
+ prepareIfChecks(node).forEach(preparedCheck => {
+ preparedCheck.check();
+ });
+ }
+ },
+
+ WhileStatement(node) {
+ prepareCheck(node, node.body, "while", "condition").check();
+ },
+
+ DoWhileStatement(node) {
+ prepareCheck(node, node.body, "do").check();
+ },
+
+ ForStatement(node) {
+ prepareCheck(node, node.body, "for", "condition").check();
+ },
+
+ ForInStatement(node) {
+ prepareCheck(node, node.body, "for-in").check();
+ },
+
+ ForOfStatement(node) {
+ prepareCheck(node, node.body, "for-of").check();
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/default-case.js b/tools/node_modules/eslint/lib/rules/default-case.js
new file mode 100644
index 0000000000..32cd8dfe49
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/default-case.js
@@ -0,0 +1,90 @@
+/**
+ * @fileoverview require default case in switch statements
+ * @author Aliaksei Shytkin
+ */
+"use strict";
+
+const DEFAULT_COMMENT_PATTERN = /^no default$/i;
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `default` cases in `switch` statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [{
+ type: "object",
+ properties: {
+ commentPattern: {
+ type: "string"
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const commentPattern = options.commentPattern
+ ? new RegExp(options.commentPattern)
+ : DEFAULT_COMMENT_PATTERN;
+
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Shortcut to get last element of array
+ * @param {*[]} collection Array
+ * @returns {*} Last element
+ */
+ function last(collection) {
+ return collection[collection.length - 1];
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ SwitchStatement(node) {
+
+ if (!node.cases.length) {
+
+ /*
+ * skip check of empty switch because there is no easy way
+ * to extract comments inside it now
+ */
+ return;
+ }
+
+ const hasDefault = node.cases.some(v => v.test === null);
+
+ if (!hasDefault) {
+
+ let comment;
+
+ const lastCase = last(node.cases);
+ const comments = sourceCode.getCommentsAfter(lastCase);
+
+ if (comments.length) {
+ comment = last(comments);
+ }
+
+ if (!comment || !commentPattern.test(comment.value.trim())) {
+ context.report({ node, message: "Expected a default case." });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/dot-location.js b/tools/node_modules/eslint/lib/rules/dot-location.js
new file mode 100644
index 0000000000..60f4af7013
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/dot-location.js
@@ -0,0 +1,88 @@
+/**
+ * @fileoverview Validates newlines before and after dots
+ * @author Greg Cochard
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent newlines before and after dots",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["object", "property"]
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const config = context.options[0];
+
+ // default to onObject if no preference is passed
+ const onObject = config === "object" || !config;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports if the dot between object and property is on the correct loccation.
+ * @param {ASTNode} obj The object owning the property.
+ * @param {ASTNode} prop The property of the object.
+ * @param {ASTNode} node The corresponding node of the token.
+ * @returns {void}
+ */
+ function checkDotLocation(obj, prop, node) {
+ const dot = sourceCode.getTokenBefore(prop);
+ const textBeforeDot = sourceCode.getText().slice(obj.range[1], dot.range[0]);
+ const textAfterDot = sourceCode.getText().slice(dot.range[1], prop.range[0]);
+
+ if (dot.type === "Punctuator" && dot.value === ".") {
+ if (onObject) {
+ if (!astUtils.isTokenOnSameLine(obj, dot)) {
+ const neededTextAfterObj = astUtils.isDecimalInteger(obj) ? " " : "";
+
+ context.report({
+ node,
+ loc: dot.loc.start,
+ message: "Expected dot to be on same line as object.",
+ fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${neededTextAfterObj}.${textBeforeDot}${textAfterDot}`)
+ });
+ }
+ } else if (!astUtils.isTokenOnSameLine(dot, prop)) {
+ context.report({
+ node,
+ loc: dot.loc.start,
+ message: "Expected dot to be on same line as property.",
+ fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
+ });
+ }
+ }
+ }
+
+ /**
+ * Checks the spacing of the dot within a member expression.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkNode(node) {
+ checkDotLocation(node.object, node.property, node);
+ }
+
+ return {
+ MemberExpression: checkNode
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/dot-notation.js b/tools/node_modules/eslint/lib/rules/dot-notation.js
new file mode 100644
index 0000000000..21e3df1741
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/dot-notation.js
@@ -0,0 +1,159 @@
+/**
+ * @fileoverview Rule to warn about using dot notation instead of square bracket notation when possible.
+ * @author Josh Perez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
+const keywords = require("../util/keywords");
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce dot notation whenever possible",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowKeywords: {
+ type: "boolean"
+ },
+ allowPattern: {
+ type: "string"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const allowKeywords = options.allowKeywords === void 0 || !!options.allowKeywords;
+ const sourceCode = context.getSourceCode();
+
+ let allowPattern;
+
+ if (options.allowPattern) {
+ allowPattern = new RegExp(options.allowPattern);
+ }
+
+ /**
+ * Check if the property is valid dot notation
+ * @param {ASTNode} node The dot notation node
+ * @param {string} value Value which is to be checked
+ * @returns {void}
+ */
+ function checkComputedProperty(node, value) {
+ if (
+ validIdentifier.test(value) &&
+ (allowKeywords || keywords.indexOf(String(value)) === -1) &&
+ !(allowPattern && allowPattern.test(value))
+ ) {
+ const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
+
+ context.report({
+ node: node.property,
+ message: "[{{propertyValue}}] is better written in dot notation.",
+ data: {
+ propertyValue: formattedValue
+ },
+ fix(fixer) {
+ const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
+ const rightBracket = sourceCode.getLastToken(node);
+
+ if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
+
+ // Don't perform any fixes if there are comments inside the brackets.
+ return null;
+ }
+
+ const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
+ const needsSpaceAfterProperty = tokenAfterProperty &&
+ rightBracket.range[1] === tokenAfterProperty.range[0] &&
+ !astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty);
+
+ const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
+ const textAfterProperty = needsSpaceAfterProperty ? " " : "";
+
+ return fixer.replaceTextRange(
+ [leftBracket.range[0], rightBracket.range[1]],
+ `${textBeforeDot}.${value}${textAfterProperty}`
+ );
+ }
+ });
+ }
+ }
+
+ return {
+ MemberExpression(node) {
+ if (
+ node.computed &&
+ node.property.type === "Literal"
+ ) {
+ checkComputedProperty(node, node.property.value);
+ }
+ if (
+ node.computed &&
+ node.property.type === "TemplateLiteral" &&
+ node.property.expressions.length === 0
+ ) {
+ checkComputedProperty(node, node.property.quasis[0].value.cooked);
+ }
+ if (
+ !allowKeywords &&
+ !node.computed &&
+ keywords.indexOf(String(node.property.name)) !== -1
+ ) {
+ context.report({
+ node: node.property,
+ message: ".{{propertyName}} is a syntax error.",
+ data: {
+ propertyName: node.property.name
+ },
+ fix(fixer) {
+ const dot = sourceCode.getTokenBefore(node.property);
+ const textAfterDot = sourceCode.text.slice(dot.range[1], node.property.range[0]);
+
+ if (textAfterDot.trim()) {
+
+ // Don't perform any fixes if there are comments between the dot and the property name.
+ return null;
+ }
+
+ if (node.object.type === "Identifier" && node.object.name === "let") {
+
+ /*
+ * A statement that starts with `let[` is parsed as a destructuring variable declaration, not
+ * a MemberExpression.
+ */
+ return null;
+ }
+
+ return fixer.replaceTextRange(
+ [dot.range[0], node.property.range[1]],
+ `[${textAfterDot}"${node.property.name}"]`
+ );
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/eol-last.js b/tools/node_modules/eslint/lib/rules/eol-last.js
new file mode 100644
index 0000000000..1f3676b0e9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/eol-last.js
@@ -0,0 +1,94 @@
+/**
+ * @fileoverview Require or disallow newline at the end of files
+ * @author Nodeca Team <https://github.com/nodeca>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow newline at the end of files",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ enum: ["always", "never", "unix", "windows"]
+ }
+ ]
+ },
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: function checkBadEOF(node) {
+ const sourceCode = context.getSourceCode(),
+ src = sourceCode.getText(),
+ location = {
+ column: lodash.last(sourceCode.lines).length,
+ line: sourceCode.lines.length
+ },
+ LF = "\n",
+ CRLF = `\r${LF}`,
+ endsWithNewline = lodash.endsWith(src, LF);
+
+ let mode = context.options[0] || "always",
+ appendCRLF = false;
+
+ if (mode === "unix") {
+
+ // `"unix"` should behave exactly as `"always"`
+ mode = "always";
+ }
+ if (mode === "windows") {
+
+ // `"windows"` should behave exactly as `"always"`, but append CRLF in the fixer for backwards compatibility
+ mode = "always";
+ appendCRLF = true;
+ }
+ if (mode === "always" && !endsWithNewline) {
+
+ // File is not newline-terminated, but should be
+ context.report({
+ node,
+ loc: location,
+ message: "Newline required at end of file but not found.",
+ fix(fixer) {
+ return fixer.insertTextAfterRange([0, src.length], appendCRLF ? CRLF : LF);
+ }
+ });
+ } else if (mode === "never" && endsWithNewline) {
+
+ // File is newline-terminated, but shouldn't be
+ context.report({
+ node,
+ loc: location,
+ message: "Newline not allowed at end of file.",
+ fix(fixer) {
+ const finalEOLs = /(?:\r?\n)+$/,
+ match = finalEOLs.exec(sourceCode.text),
+ start = match.index,
+ end = sourceCode.text.length;
+
+ return fixer.replaceTextRange([start, end], "");
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/eqeqeq.js b/tools/node_modules/eslint/lib/rules/eqeqeq.js
new file mode 100644
index 0000000000..8801102e64
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/eqeqeq.js
@@ -0,0 +1,180 @@
+/**
+ * @fileoverview Rule to flag statements that use != and == instead of !== and ===
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require the use of `===` and `!==`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always"]
+ },
+ {
+ type: "object",
+ properties: {
+ null: {
+ enum: ["always", "never", "ignore"]
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ additionalItems: false
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["smart", "allow-null"]
+ }
+ ],
+ additionalItems: false
+ }
+ ]
+ },
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const config = context.options[0] || "always";
+ const options = context.options[1] || {};
+ const sourceCode = context.getSourceCode();
+
+ const nullOption = (config === "always")
+ ? options.null || "always"
+ : "ignore";
+ const enforceRuleForNull = (nullOption === "always");
+ const enforceInverseRuleForNull = (nullOption === "never");
+
+ /**
+ * Checks if an expression is a typeof expression
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} if the node is a typeof expression
+ */
+ function isTypeOf(node) {
+ return node.type === "UnaryExpression" && node.operator === "typeof";
+ }
+
+ /**
+ * Checks if either operand of a binary expression is a typeof operation
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} if one of the operands is typeof
+ * @private
+ */
+ function isTypeOfBinary(node) {
+ return isTypeOf(node.left) || isTypeOf(node.right);
+ }
+
+ /**
+ * Checks if operands are literals of the same type (via typeof)
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} if operands are of same type
+ * @private
+ */
+ function areLiteralsAndSameType(node) {
+ return node.left.type === "Literal" && node.right.type === "Literal" &&
+ typeof node.left.value === typeof node.right.value;
+ }
+
+ /**
+ * Checks if one of the operands is a literal null
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} if operands are null
+ * @private
+ */
+ function isNullCheck(node) {
+ return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);
+ }
+
+ /**
+ * Gets the location (line and column) of the binary expression's operator
+ * @param {ASTNode} node The binary expression node to check
+ * @param {string} operator The operator to find
+ * @returns {Object} { line, column } location of operator
+ * @private
+ */
+ function getOperatorLocation(node) {
+ const opToken = sourceCode.getTokenAfter(node.left);
+
+ return { line: opToken.loc.start.line, column: opToken.loc.start.column };
+ }
+
+ /**
+ * Reports a message for this rule.
+ * @param {ASTNode} node The binary expression node that was checked
+ * @param {string} expectedOperator The operator that was expected (either '==', '!=', '===', or '!==')
+ * @returns {void}
+ * @private
+ */
+ function report(node, expectedOperator) {
+ context.report({
+ node,
+ loc: getOperatorLocation(node),
+ message: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'.",
+ data: { expectedOperator, actualOperator: node.operator },
+ fix(fixer) {
+
+ // If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix.
+ if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) {
+ const operatorToken = sourceCode.getFirstTokenBetween(
+ node.left,
+ node.right,
+ token => token.value === node.operator
+ );
+
+ return fixer.replaceText(operatorToken, expectedOperator);
+ }
+ return null;
+ }
+ });
+ }
+
+ return {
+ BinaryExpression(node) {
+ const isNull = isNullCheck(node);
+
+ if (node.operator !== "==" && node.operator !== "!=") {
+ if (enforceInverseRuleForNull && isNull) {
+ report(node, node.operator.slice(0, -1));
+ }
+ return;
+ }
+
+ if (config === "smart" && (isTypeOfBinary(node) ||
+ areLiteralsAndSameType(node) || isNull)) {
+ return;
+ }
+
+ if (!enforceRuleForNull && isNull) {
+ return;
+ }
+
+ report(node, `${node.operator}=`);
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/for-direction.js b/tools/node_modules/eslint/lib/rules/for-direction.js
new file mode 100644
index 0000000000..7178ced9db
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/for-direction.js
@@ -0,0 +1,105 @@
+/**
+ * @fileoverview enforce "for" loop update clause moving the counter in the right direction.(for-direction)
+ * @author Aladdin-ADD<hh_2013@foxmail.com>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce \"for\" loop update clause moving the counter in the right direction.",
+ category: "Possible Errors",
+ recommended: false
+ },
+ fixable: null,
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * report an error.
+ * @param {ASTNode} node the node to report.
+ * @returns {void}
+ */
+ function report(node) {
+ context.report({
+ node,
+ message: "The update clause in this loop moves the variable in the wrong direction."
+ });
+ }
+
+ /**
+ * check UpdateExpression add/sub the counter
+ * @param {ASTNode} update UpdateExpression to check
+ * @param {string} counter variable name to check
+ * @returns {int} if add return 1, if sub return -1, if nochange, return 0
+ */
+ function getUpdateDirection(update, counter) {
+ if (update.argument.type === "Identifier" && update.argument.name === counter) {
+ if (update.operator === "++") {
+ return 1;
+ }
+ if (update.operator === "--") {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * check AssignmentExpression add/sub the counter
+ * @param {ASTNode} update AssignmentExpression to check
+ * @param {string} counter variable name to check
+ * @returns {int} if add return 1, if sub return -1, if nochange, return 0
+ */
+ function getAssignmentDirection(update, counter) {
+ if (update.left.name === counter) {
+ if (update.operator === "+=") {
+ return 1;
+ }
+ if (update.operator === "-=") {
+ return -1;
+ }
+ }
+ return 0;
+ }
+ return {
+ ForStatement(node) {
+
+ if (node.test && node.test.type === "BinaryExpression" && node.test.left.type === "Identifier" && node.update) {
+ const counter = node.test.left.name;
+ const operator = node.test.operator;
+ const update = node.update;
+
+ if (operator === "<" || operator === "<=") {
+
+ // report error if update sub the counter (--, -=)
+ if (update.type === "UpdateExpression" && getUpdateDirection(update, counter) < 0) {
+ report(node);
+ }
+
+ if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) < 0) {
+ report(node);
+ }
+ } else if (operator === ">" || operator === ">=") {
+
+ // report error if update add the counter (++, +=)
+ if (update.type === "UpdateExpression" && getUpdateDirection(update, counter) > 0) {
+ report(node);
+ }
+
+ if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) > 0) {
+ report(node);
+ }
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/func-call-spacing.js b/tools/node_modules/eslint/lib/rules/func-call-spacing.js
new file mode 100644
index 0000000000..00e677d33b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/func-call-spacing.js
@@ -0,0 +1,159 @@
+/**
+ * @fileoverview Rule to control spacing within function calls
+ * @author Matt DuVall <http://www.mattduvall.com>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow spacing between function identifiers and their invocations",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["never"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always"]
+ },
+ {
+ type: "object",
+ properties: {
+ allowNewlines: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ }
+ },
+
+ create(context) {
+
+ const never = context.options[0] !== "always";
+ const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
+ const sourceCode = context.getSourceCode();
+ const text = sourceCode.getText();
+
+ /**
+ * Check if open space is present in a function name
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkSpacing(node) {
+ const lastToken = sourceCode.getLastToken(node);
+ const lastCalleeToken = sourceCode.getLastToken(node.callee);
+ const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
+ const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
+
+ // Parens in NewExpression are optional
+ if (!(parenToken && parenToken.range[1] < node.range[1])) {
+ return;
+ }
+
+ const textBetweenTokens = text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//g, "");
+ const hasWhitespace = /\s/.test(textBetweenTokens);
+ const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
+
+ /*
+ * never allowNewlines hasWhitespace hasNewline message
+ * F F F F Missing space between function name and paren.
+ * F F F T (Invalid `!hasWhitespace && hasNewline`)
+ * F F T T Unexpected newline between function name and paren.
+ * F F T F (OK)
+ * F T T F (OK)
+ * F T T T (OK)
+ * F T F T (Invalid `!hasWhitespace && hasNewline`)
+ * F T F F Missing space between function name and paren.
+ * T T F F (Invalid `never && allowNewlines`)
+ * T T F T (Invalid `!hasWhitespace && hasNewline`)
+ * T T T T (Invalid `never && allowNewlines`)
+ * T T T F (Invalid `never && allowNewlines`)
+ * T F T F Unexpected space between function name and paren.
+ * T F T T Unexpected space between function name and paren.
+ * T F F T (Invalid `!hasWhitespace && hasNewline`)
+ * T F F F (OK)
+ *
+ * T T Unexpected space between function name and paren.
+ * F F Missing space between function name and paren.
+ * F F T Unexpected newline between function name and paren.
+ */
+
+ if (never && hasWhitespace) {
+ context.report({
+ node,
+ loc: lastCalleeToken.loc.start,
+ message: "Unexpected space between function name and paren.",
+ fix(fixer) {
+
+ /*
+ * Only autofix if there is no newline
+ * https://github.com/eslint/eslint/issues/7787
+ */
+ if (!hasNewline) {
+ return fixer.removeRange([prevToken.range[1], parenToken.range[0]]);
+ }
+
+ return null;
+ }
+ });
+ } else if (!never && !hasWhitespace) {
+ context.report({
+ node,
+ loc: lastCalleeToken.loc.start,
+ message: "Missing space between function name and paren.",
+ fix(fixer) {
+ return fixer.insertTextBefore(parenToken, " ");
+ }
+ });
+ } else if (!never && !allowNewlines && hasNewline) {
+ context.report({
+ node,
+ loc: lastCalleeToken.loc.start,
+ message: "Unexpected newline between function name and paren.",
+ fix(fixer) {
+ return fixer.replaceTextRange([prevToken.range[1], parenToken.range[0]], " ");
+ }
+ });
+ }
+ }
+
+ return {
+ CallExpression: checkSpacing,
+ NewExpression: checkSpacing
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/func-name-matching.js b/tools/node_modules/eslint/lib/rules/func-name-matching.js
new file mode 100644
index 0000000000..db06d5d468
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/func-name-matching.js
@@ -0,0 +1,193 @@
+/**
+ * @fileoverview Rule to require function names to match the name of the variable or property to which they are assigned.
+ * @author Annie Zhang, Pavel Strashkin
+ */
+
+"use strict";
+
+//--------------------------------------------------------------------------
+// Requirements
+//--------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+const esutils = require("esutils");
+
+//--------------------------------------------------------------------------
+// Helpers
+//--------------------------------------------------------------------------
+
+/**
+ * Determines if a pattern is `module.exports` or `module["exports"]`
+ * @param {ASTNode} pattern The left side of the AssignmentExpression
+ * @returns {boolean} True if the pattern is `module.exports` or `module["exports"]`
+ */
+function isModuleExports(pattern) {
+ if (pattern.type === "MemberExpression" && pattern.object.type === "Identifier" && pattern.object.name === "module") {
+
+ // module.exports
+ if (pattern.property.type === "Identifier" && pattern.property.name === "exports") {
+ return true;
+ }
+
+ // module["exports"]
+ if (pattern.property.type === "Literal" && pattern.property.value === "exports") {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Determines if a string name is a valid identifier
+ * @param {string} name The string to be checked
+ * @param {int} ecmaVersion The ECMAScript version if specified in the parserOptions config
+ * @returns {boolean} True if the string is a valid identifier
+ */
+function isIdentifier(name, ecmaVersion) {
+ if (ecmaVersion >= 6) {
+ return esutils.keyword.isIdentifierES6(name);
+ }
+ return esutils.keyword.isIdentifierES5(name);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const alwaysOrNever = { enum: ["always", "never"] };
+const optionsObject = {
+ type: "object",
+ properties: {
+ includeCommonJSModuleExports: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require function names to match the name of the variable or property to which they are assigned",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [{
+ type: "array",
+ additionalItems: false,
+ items: [alwaysOrNever, optionsObject]
+ }, {
+ type: "array",
+ additionalItems: false,
+ items: [optionsObject]
+ }]
+ }
+ },
+
+ create(context) {
+ const options = (typeof context.options[0] === "object" ? context.options[0] : context.options[1]) || {};
+ const nameMatches = typeof context.options[0] === "string" ? context.options[0] : "always";
+ const includeModuleExports = options.includeCommonJSModuleExports;
+ const ecmaVersion = context.parserOptions && context.parserOptions.ecmaVersion ? context.parserOptions.ecmaVersion : 5;
+
+ /**
+ * Compares identifiers based on the nameMatches option
+ * @param {string} x the first identifier
+ * @param {string} y the second identifier
+ * @returns {boolean} whether the two identifiers should warn.
+ */
+ function shouldWarn(x, y) {
+ return (nameMatches === "always" && x !== y) || (nameMatches === "never" && x === y);
+ }
+
+ /**
+ * Reports
+ * @param {ASTNode} node The node to report
+ * @param {string} name The variable or property name
+ * @param {string} funcName The function name
+ * @param {boolean} isProp True if the reported node is a property assignment
+ * @returns {void}
+ */
+ function report(node, name, funcName, isProp) {
+ let message;
+
+ if (nameMatches === "always" && isProp) {
+ message = "Function name `{{funcName}}` should match property name `{{name}}`";
+ } else if (nameMatches === "always") {
+ message = "Function name `{{funcName}}` should match variable name `{{name}}`";
+ } else if (isProp) {
+ message = "Function name `{{funcName}}` should not match property name `{{name}}`";
+ } else {
+ message = "Function name `{{funcName}}` should not match variable name `{{name}}`";
+ }
+ context.report({
+ node,
+ message,
+ data: {
+ name,
+ funcName
+ }
+ });
+ }
+
+ /**
+ * Determines whether a given node is a string literal
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} `true` if the node is a string literal
+ */
+ function isStringLiteral(node) {
+ return node.type === "Literal" && typeof node.value === "string";
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ VariableDeclarator(node) {
+ if (!node.init || node.init.type !== "FunctionExpression" || node.id.type !== "Identifier") {
+ return;
+ }
+ if (node.init.id && shouldWarn(node.id.name, node.init.id.name)) {
+ report(node, node.id.name, node.init.id.name, false);
+ }
+ },
+
+ AssignmentExpression(node) {
+ if (
+ node.right.type !== "FunctionExpression" ||
+ (node.left.computed && node.left.property.type !== "Literal") ||
+ (!includeModuleExports && isModuleExports(node.left)) ||
+ (node.left.type !== "Identifier" && node.left.type !== "MemberExpression")
+ ) {
+ return;
+ }
+
+ const isProp = node.left.type === "MemberExpression";
+ const name = isProp ? astUtils.getStaticPropertyName(node.left) : node.left.name;
+
+ if (node.right.id && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {
+ report(node, name, node.right.id.name, isProp);
+ }
+ },
+
+ Property(node) {
+ if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
+ return;
+ }
+ if (node.key.type === "Identifier" && shouldWarn(node.key.name, node.value.id.name)) {
+ report(node, node.key.name, node.value.id.name, true);
+ } else if (
+ isStringLiteral(node.key) &&
+ isIdentifier(node.key.value, ecmaVersion) &&
+ shouldWarn(node.key.value, node.value.id.name)
+ ) {
+ report(node, node.key.value, node.value.id.name, true);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/func-names.js b/tools/node_modules/eslint/lib/rules/func-names.js
new file mode 100644
index 0000000000..848ce97574
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/func-names.js
@@ -0,0 +1,114 @@
+/**
+ * @fileoverview Rule to warn when a function expression does not have a name.
+ * @author Kyle T. Nunery
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+/**
+ * Checks whether or not a given variable is a function name.
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @returns {boolean} `true` if the variable is a function name.
+ */
+function isFunctionName(variable) {
+ return variable && variable.defs[0].type === "FunctionName";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow named `function` expressions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["always", "as-needed", "never"]
+ }
+ ]
+ },
+
+ create(context) {
+ const never = context.options[0] === "never";
+ const asNeeded = context.options[0] === "as-needed";
+
+ /**
+ * Determines whether the current FunctionExpression node is a get, set, or
+ * shorthand method in an object literal or a class.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} True if the node is a get, set, or shorthand method.
+ */
+ function isObjectOrClassMethod(node) {
+ const parent = node.parent;
+
+ return (parent.type === "MethodDefinition" || (
+ parent.type === "Property" && (
+ parent.method ||
+ parent.kind === "get" ||
+ parent.kind === "set"
+ )
+ ));
+ }
+
+ /**
+ * Determines whether the current FunctionExpression node has a name that would be
+ * inferred from context in a conforming ES6 environment.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} True if the node would have a name assigned automatically.
+ */
+ function hasInferredName(node) {
+ const parent = node.parent;
+
+ return isObjectOrClassMethod(node) ||
+ (parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
+ (parent.type === "Property" && parent.value === node) ||
+ (parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
+ (parent.type === "ExportDefaultDeclaration" && parent.declaration === node) ||
+ (parent.type === "AssignmentPattern" && parent.right === node);
+ }
+
+ return {
+ "FunctionExpression:exit"(node) {
+
+ // Skip recursive functions.
+ const nameVar = context.getDeclaredVariables(node)[0];
+
+ if (isFunctionName(nameVar) && nameVar.references.length > 0) {
+ return;
+ }
+
+ const hasName = Boolean(node.id && node.id.name);
+ const name = astUtils.getFunctionNameWithKind(node);
+
+ if (never) {
+ if (hasName) {
+ context.report({
+ node,
+ message: "Unexpected named {{name}}.",
+ data: { name }
+ });
+ }
+ } else {
+ if (!hasName && (asNeeded ? !hasInferredName(node) : !isObjectOrClassMethod(node))) {
+ context.report({
+ node,
+ message: "Unexpected unnamed {{name}}.",
+ data: { name }
+ });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/func-style.js b/tools/node_modules/eslint/lib/rules/func-style.js
new file mode 100644
index 0000000000..123eae3d8a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/func-style.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview Rule to enforce a particular function style
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the consistent use of either `function` declarations or expressions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["declaration", "expression"]
+ },
+ {
+ type: "object",
+ properties: {
+ allowArrowFunctions: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const style = context.options[0],
+ allowArrowFunctions = context.options[1] && context.options[1].allowArrowFunctions === true,
+ enforceDeclarations = (style === "declaration"),
+ stack = [];
+
+ const nodesToCheck = {
+ FunctionDeclaration(node) {
+ stack.push(false);
+
+ if (!enforceDeclarations && node.parent.type !== "ExportDefaultDeclaration") {
+ context.report({ node, message: "Expected a function expression." });
+ }
+ },
+ "FunctionDeclaration:exit"() {
+ stack.pop();
+ },
+
+ FunctionExpression(node) {
+ stack.push(false);
+
+ if (enforceDeclarations && node.parent.type === "VariableDeclarator") {
+ context.report({ node: node.parent, message: "Expected a function declaration." });
+ }
+ },
+ "FunctionExpression:exit"() {
+ stack.pop();
+ },
+
+ ThisExpression() {
+ if (stack.length > 0) {
+ stack[stack.length - 1] = true;
+ }
+ }
+ };
+
+ if (!allowArrowFunctions) {
+ nodesToCheck.ArrowFunctionExpression = function() {
+ stack.push(false);
+ };
+
+ nodesToCheck["ArrowFunctionExpression:exit"] = function(node) {
+ const hasThisExpr = stack.pop();
+
+ if (enforceDeclarations && !hasThisExpr && node.parent.type === "VariableDeclarator") {
+ context.report({ node: node.parent, message: "Expected a function declaration." });
+ }
+ };
+ }
+
+ return nodesToCheck;
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/function-paren-newline.js b/tools/node_modules/eslint/lib/rules/function-paren-newline.js
new file mode 100644
index 0000000000..10ad960a4d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/function-paren-newline.js
@@ -0,0 +1,221 @@
+/**
+ * @fileoverview enforce consistent line breaks inside function parentheses
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent line breaks inside function parentheses",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never", "consistent", "multiline"]
+ },
+ {
+ type: "object",
+ properties: {
+ minItems: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const rawOption = context.options[0] || "multiline";
+ const multilineOption = rawOption === "multiline";
+ const consistentOption = rawOption === "consistent";
+ let minItems;
+
+ if (typeof rawOption === "object") {
+ minItems = rawOption.minItems;
+ } else if (rawOption === "always") {
+ minItems = 0;
+ } else if (rawOption === "never") {
+ minItems = Infinity;
+ } else {
+ minItems = null;
+ }
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Determines whether there should be newlines inside function parens
+ * @param {ASTNode[]} elements The arguments or parameters in the list
+ * @param {boolean} hasLeftNewline `true` if the left paren has a newline in the current code.
+ * @returns {boolean} `true` if there should be newlines inside the function parens
+ */
+ function shouldHaveNewlines(elements, hasLeftNewline) {
+ if (multilineOption) {
+ return elements.some((element, index) => index !== elements.length - 1 && element.loc.end.line !== elements[index + 1].loc.start.line);
+ }
+ if (consistentOption) {
+ return hasLeftNewline;
+ }
+ return elements.length >= minItems;
+ }
+
+ /**
+ * Validates a list of arguments or parameters
+ * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
+ * @param {ASTNode[]} elements The arguments or parameters in the list
+ * @returns {void}
+ */
+ function validateParens(parens, elements) {
+ const leftParen = parens.leftParen;
+ const rightParen = parens.rightParen;
+ const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParen);
+ const tokenBeforeRightParen = sourceCode.getTokenBefore(rightParen);
+ const hasLeftNewline = !astUtils.isTokenOnSameLine(leftParen, tokenAfterLeftParen);
+ const hasRightNewline = !astUtils.isTokenOnSameLine(tokenBeforeRightParen, rightParen);
+ const needsNewlines = shouldHaveNewlines(elements, hasLeftNewline);
+
+ if (hasLeftNewline && !needsNewlines) {
+ context.report({
+ node: leftParen,
+ message: "Unexpected newline after '('.",
+ fix(fixer) {
+ return sourceCode.getText().slice(leftParen.range[1], tokenAfterLeftParen.range[0]).trim()
+
+ // If there is a comment between the ( and the first element, don't do a fix.
+ ? null
+ : fixer.removeRange([leftParen.range[1], tokenAfterLeftParen.range[0]]);
+ }
+ });
+ } else if (!hasLeftNewline && needsNewlines) {
+ context.report({
+ node: leftParen,
+ message: "Expected a newline after '('.",
+ fix: fixer => fixer.insertTextAfter(leftParen, "\n")
+ });
+ }
+
+ if (hasRightNewline && !needsNewlines) {
+ context.report({
+ node: rightParen,
+ message: "Unexpected newline before ')'.",
+ fix(fixer) {
+ return sourceCode.getText().slice(tokenBeforeRightParen.range[1], rightParen.range[0]).trim()
+
+ // If there is a comment between the last element and the ), don't do a fix.
+ ? null
+ : fixer.removeRange([tokenBeforeRightParen.range[1], rightParen.range[0]]);
+ }
+ });
+ } else if (!hasRightNewline && needsNewlines) {
+ context.report({
+ node: rightParen,
+ message: "Expected a newline before ')'.",
+ fix: fixer => fixer.insertTextBefore(rightParen, "\n")
+ });
+ }
+ }
+
+ /**
+ * Gets the left paren and right paren tokens of a node.
+ * @param {ASTNode} node The node with parens
+ * @returns {Object} An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token.
+ * Can also return `null` if an expression has no parens (e.g. a NewExpression with no arguments, or an ArrowFunctionExpression
+ * with a single parameter)
+ */
+ function getParenTokens(node) {
+ switch (node.type) {
+ case "NewExpression":
+ if (!node.arguments.length && !(
+ astUtils.isOpeningParenToken(sourceCode.getLastToken(node, { skip: 1 })) &&
+ astUtils.isClosingParenToken(sourceCode.getLastToken(node))
+ )) {
+
+ // If the NewExpression does not have parens (e.g. `new Foo`), return null.
+ return null;
+ }
+
+ // falls through
+
+ case "CallExpression":
+ return {
+ leftParen: sourceCode.getTokenAfter(node.callee, astUtils.isOpeningParenToken),
+ rightParen: sourceCode.getLastToken(node)
+ };
+
+ case "FunctionDeclaration":
+ case "FunctionExpression": {
+ const leftParen = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
+ const rightParen = node.params.length
+ ? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken)
+ : sourceCode.getTokenAfter(leftParen);
+
+ return { leftParen, rightParen };
+ }
+
+ case "ArrowFunctionExpression": {
+ const firstToken = sourceCode.getFirstToken(node);
+
+ if (!astUtils.isOpeningParenToken(firstToken)) {
+
+ // If the ArrowFunctionExpression has a single param without parens, return null.
+ return null;
+ }
+
+ return {
+ leftParen: firstToken,
+ rightParen: sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken)
+ };
+ }
+
+ default:
+ throw new TypeError(`unexpected node with type ${node.type}`);
+ }
+ }
+
+ /**
+ * Validates the parentheses for a node
+ * @param {ASTNode} node The node with parens
+ * @returns {void}
+ */
+ function validateNode(node) {
+ const parens = getParenTokens(node);
+
+ if (parens) {
+ validateParens(parens, astUtils.isFunction(node) ? node.params : node.arguments);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ ArrowFunctionExpression: validateNode,
+ CallExpression: validateNode,
+ FunctionDeclaration: validateNode,
+ FunctionExpression: validateNode,
+ NewExpression: validateNode
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/generator-star-spacing.js b/tools/node_modules/eslint/lib/rules/generator-star-spacing.js
new file mode 100644
index 0000000000..a718b59afc
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/generator-star-spacing.js
@@ -0,0 +1,199 @@
+/**
+ * @fileoverview Rule to check the spacing around the * in generator functions.
+ * @author Jamund Ferguson
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const OVERRIDE_SCHEMA = {
+ oneOf: [
+ {
+ enum: ["before", "after", "both", "neither"]
+ },
+ {
+ type: "object",
+ properties: {
+ before: { type: "boolean" },
+ after: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing around `*` operators in generator functions",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["before", "after", "both", "neither"]
+ },
+ {
+ type: "object",
+ properties: {
+ before: { type: "boolean" },
+ after: { type: "boolean" },
+ named: OVERRIDE_SCHEMA,
+ anonymous: OVERRIDE_SCHEMA,
+ method: OVERRIDE_SCHEMA
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const optionDefinitions = {
+ before: { before: true, after: false },
+ after: { before: false, after: true },
+ both: { before: true, after: true },
+ neither: { before: false, after: false }
+ };
+
+ /**
+ * Returns resolved option definitions based on an option and defaults
+ *
+ * @param {any} option - The option object or string value
+ * @param {Object} defaults - The defaults to use if options are not present
+ * @returns {Object} the resolved object definition
+ */
+ function optionToDefinition(option, defaults) {
+ if (!option) {
+ return defaults;
+ }
+
+ return typeof option === "string"
+ ? optionDefinitions[option]
+ : Object.assign({}, defaults, option);
+ }
+
+ const modes = (function(option) {
+ option = option || {};
+ const defaults = optionToDefinition(option, optionDefinitions.before);
+
+ return {
+ named: optionToDefinition(option.named, defaults),
+ anonymous: optionToDefinition(option.anonymous, defaults),
+ method: optionToDefinition(option.method, defaults)
+ };
+ }(context.options[0]));
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Checks if the given token is a star token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a star token.
+ */
+ function isStarToken(token) {
+ return token.value === "*" && token.type === "Punctuator";
+ }
+
+ /**
+ * Gets the generator star token of the given function node.
+ *
+ * @param {ASTNode} node - The function node to get.
+ * @returns {Token} Found star token.
+ */
+ function getStarToken(node) {
+ return sourceCode.getFirstToken(
+ (node.parent.method || node.parent.type === "MethodDefinition") ? node.parent : node,
+ isStarToken
+ );
+ }
+
+ /**
+ * Checks the spacing between two tokens before or after the star token.
+ *
+ * @param {string} kind Either "named", "anonymous", or "method"
+ * @param {string} side Either "before" or "after".
+ * @param {Token} leftToken `function` keyword token if side is "before", or
+ * star token if side is "after".
+ * @param {Token} rightToken Star token if side is "before", or identifier
+ * token if side is "after".
+ * @returns {void}
+ */
+ function checkSpacing(kind, side, leftToken, rightToken) {
+ if (!!(rightToken.range[0] - leftToken.range[1]) !== modes[kind][side]) {
+ const after = leftToken.value === "*";
+ const spaceRequired = modes[kind][side];
+ const node = after ? leftToken : rightToken;
+ const type = spaceRequired ? "Missing" : "Unexpected";
+ const message = "{{type}} space {{side}} *.";
+ const data = {
+ type,
+ side
+ };
+
+ context.report({
+ node,
+ message,
+ data,
+ fix(fixer) {
+ if (spaceRequired) {
+ if (after) {
+ return fixer.insertTextAfter(node, " ");
+ }
+ return fixer.insertTextBefore(node, " ");
+ }
+ return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
+ }
+ });
+ }
+ }
+
+ /**
+ * Enforces the spacing around the star if node is a generator function.
+ *
+ * @param {ASTNode} node A function expression or declaration node.
+ * @returns {void}
+ */
+ function checkFunction(node) {
+ if (!node.generator) {
+ return;
+ }
+
+ const starToken = getStarToken(node);
+ const prevToken = sourceCode.getTokenBefore(starToken);
+ const nextToken = sourceCode.getTokenAfter(starToken);
+
+ let kind = "named";
+
+ if (node.parent.type === "MethodDefinition" || (node.parent.type === "Property" && node.parent.method)) {
+ kind = "method";
+ } else if (!node.id) {
+ kind = "anonymous";
+ }
+
+ // Only check before when preceded by `function`|`static` keyword
+ if (!(kind === "method" && starToken === sourceCode.getFirstToken(node.parent))) {
+ checkSpacing(kind, "before", prevToken, starToken);
+ }
+
+ checkSpacing(kind, "after", starToken, nextToken);
+ }
+
+ return {
+ FunctionDeclaration: checkFunction,
+ FunctionExpression: checkFunction
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/getter-return.js b/tools/node_modules/eslint/lib/rules/getter-return.js
new file mode 100644
index 0000000000..6eb1efc00c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/getter-return.js
@@ -0,0 +1,177 @@
+/**
+ * @fileoverview Enforces that a return statement is present in property getters.
+ * @author Aladdin-ADD(hh_2013@foxmail.com)
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/;
+
+/**
+ * Checks a given code path segment is reachable.
+ *
+ * @param {CodePathSegment} segment - A segment to check.
+ * @returns {boolean} `true` if the segment is reachable.
+ */
+function isReachable(segment) {
+ return segment.reachable;
+}
+
+/**
+ * Gets a readable location.
+ *
+ * - FunctionExpression -> the function name or `function` keyword.
+ *
+ * @param {ASTNode} node - A function node to get.
+ * @returns {ASTNode|Token} The node or the token of a location.
+ */
+function getId(node) {
+ return node.id || node;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce `return` statements in getters",
+ category: "Possible Errors",
+ recommended: false
+ },
+ fixable: null,
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowImplicit: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = context.options[0] || { allowImplicit: false };
+
+ let funcInfo = {
+ upper: null,
+ codePath: null,
+ hasReturn: false,
+ shouldCheck: false,
+ node: null
+ };
+
+ /**
+ * Checks whether or not the last code path segment is reachable.
+ * Then reports this function if the segment is reachable.
+ *
+ * If the last code path segment is reachable, there are paths which are not
+ * returned or thrown.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {void}
+ */
+ function checkLastSegment(node) {
+ if (funcInfo.shouldCheck &&
+ funcInfo.codePath.currentSegments.some(isReachable)
+ ) {
+ context.report({
+ node,
+ loc: getId(node).loc.start,
+ message: funcInfo.hasReturn
+ ? "Expected {{name}} to always return a value."
+ : "Expected to return a value in {{name}}.",
+ data: {
+ name: astUtils.getFunctionNameWithKind(funcInfo.node)
+ }
+ });
+ }
+ }
+
+ /**
+ * Checks whether a node means a getter function.
+ * @param {ASTNode} node - a node to check.
+ * @returns {boolean} if node means a getter, return true; else return false.
+ */
+ function isGetter(node) {
+ const parent = node.parent;
+
+ if (TARGET_NODE_TYPE.test(node.type) && node.body.type === "BlockStatement") {
+ if (parent.kind === "get") {
+ return true;
+ }
+ if (parent.type === "Property" && astUtils.getStaticPropertyName(parent) === "get" && parent.parent.type === "ObjectExpression") {
+
+ // Object.defineProperty()
+ if (parent.parent.parent.type === "CallExpression" &&
+ astUtils.getStaticPropertyName(parent.parent.parent.callee) === "defineProperty") {
+ return true;
+ }
+
+ // Object.defineProperties()
+ if (parent.parent.parent.type === "Property" &&
+ parent.parent.parent.parent.type === "ObjectExpression" &&
+ parent.parent.parent.parent.parent.type === "CallExpression" &&
+ astUtils.getStaticPropertyName(parent.parent.parent.parent.parent.callee) === "defineProperties") {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ return {
+
+ // Stacks this function's information.
+ onCodePathStart(codePath, node) {
+ funcInfo = {
+ upper: funcInfo,
+ codePath,
+ hasReturn: false,
+ shouldCheck: isGetter(node),
+ node
+ };
+ },
+
+ // Pops this function's information.
+ onCodePathEnd() {
+ funcInfo = funcInfo.upper;
+ },
+
+ // Checks the return statement is valid.
+ ReturnStatement(node) {
+ if (funcInfo.shouldCheck) {
+ funcInfo.hasReturn = true;
+
+ // if allowImplicit: false, should also check node.argument
+ if (!options.allowImplicit && !node.argument) {
+ context.report({
+ node,
+ message: "Expected to return a value in {{name}}.",
+ data: {
+ name: astUtils.getFunctionNameWithKind(funcInfo.node)
+ }
+ });
+ }
+ }
+ },
+
+ // Reports a given function if the last path is reachable.
+ "FunctionExpression:exit": checkLastSegment,
+ "ArrowFunctionExpression:exit": checkLastSegment
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/global-require.js b/tools/node_modules/eslint/lib/rules/global-require.js
new file mode 100644
index 0000000000..beda8d99f1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/global-require.js
@@ -0,0 +1,75 @@
+/**
+ * @fileoverview Rule for disallowing require() outside of the top-level module context
+ * @author Jamund Ferguson
+ */
+
+"use strict";
+
+const ACCEPTABLE_PARENTS = [
+ "AssignmentExpression",
+ "VariableDeclarator",
+ "MemberExpression",
+ "ExpressionStatement",
+ "CallExpression",
+ "ConditionalExpression",
+ "Program",
+ "VariableDeclaration"
+];
+
+/**
+ * Finds the eslint-scope reference in the given scope.
+ * @param {Object} scope The scope to search.
+ * @param {ASTNode} node The identifier node.
+ * @returns {Reference|null} Returns the found reference or null if none were found.
+ */
+function findReference(scope, node) {
+ const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] &&
+ reference.identifier.range[1] === node.range[1]);
+
+ /* istanbul ignore else: correctly returns null */
+ if (references.length === 1) {
+ return references[0];
+ }
+ return null;
+
+}
+
+/**
+ * Checks if the given identifier node is shadowed in the given scope.
+ * @param {Object} scope The current scope.
+ * @param {ASTNode} node The identifier node to check.
+ * @returns {boolean} Whether or not the name is shadowed.
+ */
+function isShadowed(scope, node) {
+ const reference = findReference(scope, node);
+
+ return reference && reference.resolved && reference.resolved.defs.length > 0;
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `require()` calls to be placed at top-level module scope",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ return {
+ CallExpression(node) {
+ const currentScope = context.getScope();
+
+ if (node.callee.name === "require" && !isShadowed(currentScope, node.callee)) {
+ const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.indexOf(parent.type) > -1);
+
+ if (!isGoodRequire) {
+ context.report({ node, message: "Unexpected require()." });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/guard-for-in.js b/tools/node_modules/eslint/lib/rules/guard-for-in.js
new file mode 100644
index 0000000000..754830f6a6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/guard-for-in.js
@@ -0,0 +1,42 @@
+/**
+ * @fileoverview Rule to flag for-in loops without if statements inside
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `for-in` loops to include an `if` statement",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ ForInStatement(node) {
+
+ /*
+ * If the for-in statement has {}, then the real body is the body
+ * of the BlockStatement. Otherwise, just use body as provided.
+ */
+ const body = node.body.type === "BlockStatement" ? node.body.body[0] : node.body;
+
+ if (body && body.type !== "IfStatement") {
+ context.report({ node, message: "The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/handle-callback-err.js b/tools/node_modules/eslint/lib/rules/handle-callback-err.js
new file mode 100644
index 0000000000..de36a0c1b0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/handle-callback-err.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview Ensure handling of errors when we know they exist.
+ * @author Jamund Ferguson
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require error handling in callbacks",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "string"
+ }
+ ]
+ },
+
+ create(context) {
+
+ const errorArgument = context.options[0] || "err";
+
+ /**
+ * Checks if the given argument should be interpreted as a regexp pattern.
+ * @param {string} stringToCheck The string which should be checked.
+ * @returns {boolean} Whether or not the string should be interpreted as a pattern.
+ */
+ function isPattern(stringToCheck) {
+ const firstChar = stringToCheck[0];
+
+ return firstChar === "^";
+ }
+
+ /**
+ * Checks if the given name matches the configured error argument.
+ * @param {string} name The name which should be compared.
+ * @returns {boolean} Whether or not the given name matches the configured error variable name.
+ */
+ function matchesConfiguredErrorName(name) {
+ if (isPattern(errorArgument)) {
+ const regexp = new RegExp(errorArgument);
+
+ return regexp.test(name);
+ }
+ return name === errorArgument;
+ }
+
+ /**
+ * Get the parameters of a given function scope.
+ * @param {Object} scope The function scope.
+ * @returns {array} All parameters of the given scope.
+ */
+ function getParameters(scope) {
+ return scope.variables.filter(variable => variable.defs[0] && variable.defs[0].type === "Parameter");
+ }
+
+ /**
+ * Check to see if we're handling the error object properly.
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ */
+ function checkForError(node) {
+ const scope = context.getScope(),
+ parameters = getParameters(scope),
+ firstParameter = parameters[0];
+
+ if (firstParameter && matchesConfiguredErrorName(firstParameter.name)) {
+ if (firstParameter.references.length === 0) {
+ context.report({ node, message: "Expected error to be handled." });
+ }
+ }
+ }
+
+ return {
+ FunctionDeclaration: checkForError,
+ FunctionExpression: checkForError,
+ ArrowFunctionExpression: checkForError
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/id-blacklist.js b/tools/node_modules/eslint/lib/rules/id-blacklist.js
new file mode 100644
index 0000000000..ee28c0b5a8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/id-blacklist.js
@@ -0,0 +1,121 @@
+/**
+ * @fileoverview Rule that warns when identifier names that are
+ * blacklisted in the configuration are used.
+ * @author Keith Cirkel (http://keithcirkel.co.uk)
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow specified identifiers",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: {
+ type: "array",
+ items: {
+ type: "string"
+ },
+ uniqueItems: true
+ }
+ },
+
+ create(context) {
+
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const blacklist = context.options;
+
+
+ /**
+ * Checks if a string matches the provided pattern
+ * @param {string} name The string to check.
+ * @returns {boolean} if the string is a match
+ * @private
+ */
+ function isInvalid(name) {
+ return blacklist.indexOf(name) !== -1;
+ }
+
+ /**
+ * Verifies if we should report an error or not based on the effective
+ * parent node and the identifier name.
+ * @param {ASTNode} effectiveParent The effective parent node of the node to be reported
+ * @param {string} name The identifier name of the identifier node
+ * @returns {boolean} whether an error should be reported or not
+ */
+ function shouldReport(effectiveParent, name) {
+ return effectiveParent.type !== "CallExpression" &&
+ effectiveParent.type !== "NewExpression" &&
+ isInvalid(name);
+ }
+
+ /**
+ * Reports an AST node as a rule violation.
+ * @param {ASTNode} node The node to report.
+ * @returns {void}
+ * @private
+ */
+ function report(node) {
+ context.report({
+ node,
+ message: "Identifier '{{name}}' is blacklisted.",
+ data: {
+ name: node.name
+ }
+ });
+ }
+
+ return {
+
+ Identifier(node) {
+ const name = node.name,
+ effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
+
+ // MemberExpressions get special rules
+ if (node.parent.type === "MemberExpression") {
+
+ // Always check object names
+ if (node.parent.object.type === "Identifier" &&
+ node.parent.object.name === node.name) {
+ if (isInvalid(name)) {
+ report(node);
+ }
+
+ // Report AssignmentExpressions only if they are the left side of the assignment
+ } else if (effectiveParent.type === "AssignmentExpression" &&
+ (effectiveParent.right.type !== "MemberExpression" ||
+ effectiveParent.left.type === "MemberExpression" &&
+ effectiveParent.left.property.name === node.name)) {
+ if (isInvalid(name)) {
+ report(node);
+ }
+ }
+
+ // Properties have their own rules
+ } else if (node.parent.type === "Property") {
+
+ if (shouldReport(effectiveParent, name)) {
+ report(node);
+ }
+
+ // Report anything that is a match and not a CallExpression
+ } else if (shouldReport(effectiveParent, name)) {
+ report(node);
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/id-length.js b/tools/node_modules/eslint/lib/rules/id-length.js
new file mode 100644
index 0000000000..dad9c40649
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/id-length.js
@@ -0,0 +1,116 @@
+/**
+ * @fileoverview Rule that warns when identifier names are shorter or longer
+ * than the values provided in configuration.
+ * @author Burak Yigit Kaya aka BYK
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce minimum and maximum identifier lengths",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ min: {
+ type: "number"
+ },
+ max: {
+ type: "number"
+ },
+ exceptions: {
+ type: "array",
+ uniqueItems: true,
+ items: {
+ type: "string"
+ }
+ },
+ properties: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const minLength = typeof options.min !== "undefined" ? options.min : 2;
+ const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
+ const properties = options.properties !== "never";
+ const exceptions = (options.exceptions ? options.exceptions : [])
+ .reduce((obj, item) => {
+ obj[item] = true;
+
+ return obj;
+ }, {});
+
+ const SUPPORTED_EXPRESSIONS = {
+ MemberExpression: properties && function(parent) {
+ return !parent.computed && (
+
+ // regular property assignment
+ (parent.parent.left === parent && parent.parent.type === "AssignmentExpression" ||
+
+ // or the last identifier in an ObjectPattern destructuring
+ parent.parent.type === "Property" && parent.parent.value === parent &&
+ parent.parent.parent.type === "ObjectPattern" && parent.parent.parent.parent.left === parent.parent.parent)
+ );
+ },
+ AssignmentPattern(parent, node) {
+ return parent.left === node;
+ },
+ VariableDeclarator(parent, node) {
+ return parent.id === node;
+ },
+ Property: properties && function(parent, node) {
+ return parent.key === node;
+ },
+ ImportDefaultSpecifier: true,
+ RestElement: true,
+ FunctionExpression: true,
+ ArrowFunctionExpression: true,
+ ClassDeclaration: true,
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ CatchClause: true
+ };
+
+ return {
+ Identifier(node) {
+ const name = node.name;
+ const parent = node.parent;
+
+ const isShort = name.length < minLength;
+ const isLong = name.length > maxLength;
+
+ if (!(isShort || isLong) || exceptions[name]) {
+ return; // Nothing to report
+ }
+
+ const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
+
+ if (isValidExpression && (isValidExpression === true || isValidExpression(parent, node))) {
+ context.report({
+ node,
+ message: isShort
+ ? "Identifier name '{{name}}' is too short (< {{min}})."
+ : "Identifier name '{{name}}' is too long (> {{max}}).",
+ data: { name, min: minLength, max: maxLength }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/id-match.js b/tools/node_modules/eslint/lib/rules/id-match.js
new file mode 100644
index 0000000000..0420fdc74e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/id-match.js
@@ -0,0 +1,144 @@
+/**
+ * @fileoverview Rule to flag non-matching identifiers
+ * @author Matthieu Larcher
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require identifiers to match a specified regular expression",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "string"
+ },
+ {
+ type: "object",
+ properties: {
+ properties: {
+ type: "boolean"
+ }
+ }
+ }
+ ]
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const pattern = context.options[0] || "^.+$",
+ regexp = new RegExp(pattern);
+
+ const options = context.options[1] || {},
+ properties = !!options.properties,
+ onlyDeclarations = !!options.onlyDeclarations;
+
+ /**
+ * Checks if a string matches the provided pattern
+ * @param {string} name The string to check.
+ * @returns {boolean} if the string is a match
+ * @private
+ */
+ function isInvalid(name) {
+ return !regexp.test(name);
+ }
+
+ /**
+ * Verifies if we should report an error or not based on the effective
+ * parent node and the identifier name.
+ * @param {ASTNode} effectiveParent The effective parent node of the node to be reported
+ * @param {string} name The identifier name of the identifier node
+ * @returns {boolean} whether an error should be reported or not
+ */
+ function shouldReport(effectiveParent, name) {
+ return effectiveParent.type !== "CallExpression" &&
+ effectiveParent.type !== "NewExpression" &&
+ isInvalid(name);
+ }
+
+ /**
+ * Reports an AST node as a rule violation.
+ * @param {ASTNode} node The node to report.
+ * @returns {void}
+ * @private
+ */
+ function report(node) {
+ context.report({
+ node,
+ message: "Identifier '{{name}}' does not match the pattern '{{pattern}}'.",
+ data: {
+ name: node.name,
+ pattern
+ }
+ });
+ }
+
+ return {
+
+ Identifier(node) {
+ const name = node.name,
+ parent = node.parent,
+ effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;
+
+ if (parent.type === "MemberExpression") {
+
+ if (!properties) {
+ return;
+ }
+
+ // Always check object names
+ if (parent.object.type === "Identifier" &&
+ parent.object.name === name) {
+ if (isInvalid(name)) {
+ report(node);
+ }
+
+ // Report AssignmentExpressions only if they are the left side of the assignment
+ } else if (effectiveParent.type === "AssignmentExpression" &&
+ (effectiveParent.right.type !== "MemberExpression" ||
+ effectiveParent.left.type === "MemberExpression" &&
+ effectiveParent.left.property.name === name)) {
+ if (isInvalid(name)) {
+ report(node);
+ }
+ }
+
+ } else if (parent.type === "Property") {
+
+ if (!properties || parent.key.name !== name) {
+ return;
+ }
+
+ if (shouldReport(effectiveParent, name)) {
+ report(node);
+ }
+
+ } else {
+ const isDeclaration = effectiveParent.type === "FunctionDeclaration" || effectiveParent.type === "VariableDeclarator";
+
+ if (onlyDeclarations && !isDeclaration) {
+ return;
+ }
+
+ if (shouldReport(effectiveParent, name)) {
+ report(node);
+ }
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/implicit-arrow-linebreak.js b/tools/node_modules/eslint/lib/rules/implicit-arrow-linebreak.js
new file mode 100644
index 0000000000..b8802f4de5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/implicit-arrow-linebreak.js
@@ -0,0 +1,86 @@
+/**
+ * @fileoverview enforce the location of arrow function bodies
+ * @author Sharmila Jesupaul
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the location of arrow function bodies",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ enum: ["beside", "below"]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+ /**
+ * Gets the applicable preference for a particular keyword
+ * @returns {string} The applicable option for the keyword, e.g. 'beside'
+ */
+ function getOption() {
+ return context.options[0] || "beside";
+ }
+
+ /**
+ * Validates the location of an arrow function body
+ * @param {ASTNode} node The arrow function body
+ * @param {string} keywordName The applicable keyword name for the arrow function body
+ * @returns {void}
+ */
+ function validateExpression(node) {
+ const option = getOption();
+
+ let tokenBefore = sourceCode.getTokenBefore(node.body);
+ const hasParens = tokenBefore.value === "(";
+
+ if (node.type === "BlockStatement") {
+ return;
+ }
+
+ let fixerTarget = node.body;
+
+ if (hasParens) {
+
+ // Gets the first token before the function body that is not an open paren
+ tokenBefore = sourceCode.getTokenBefore(node.body, token => token.value !== "(");
+ fixerTarget = sourceCode.getTokenAfter(tokenBefore);
+ }
+
+ if (tokenBefore.loc.end.line === fixerTarget.loc.start.line && option === "below") {
+ context.report({
+ node: fixerTarget,
+ message: "Expected a linebreak before this expression.",
+ fix: fixer => fixer.insertTextBefore(fixerTarget, "\n")
+ });
+ } else if (tokenBefore.loc.end.line !== fixerTarget.loc.start.line && option === "beside") {
+ context.report({
+ node: fixerTarget,
+ message: "Expected no linebreak before this expression.",
+ fix: fixer => fixer.replaceTextRange([tokenBefore.range[1], fixerTarget.range[0]], " ")
+ });
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+ return {
+ ArrowFunctionExpression: node => validateExpression(node)
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/indent-legacy.js b/tools/node_modules/eslint/lib/rules/indent-legacy.js
new file mode 100644
index 0000000000..cf91406806
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/indent-legacy.js
@@ -0,0 +1,1137 @@
+/**
+ * @fileoverview This option sets a specific tab width for your code
+ *
+ * This rule has been ported and modified from nodeca.
+ * @author Vitaly Puzrin
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+/* istanbul ignore next: this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway. */
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent indentation",
+ category: "Stylistic Issues",
+ recommended: false,
+ replacedBy: ["indent"]
+ },
+
+ deprecated: true,
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["tab"]
+ },
+ {
+ type: "integer",
+ minimum: 0
+ }
+ ]
+ },
+ {
+ type: "object",
+ properties: {
+ SwitchCase: {
+ type: "integer",
+ minimum: 0
+ },
+ VariableDeclarator: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ var: {
+ type: "integer",
+ minimum: 0
+ },
+ let: {
+ type: "integer",
+ minimum: 0
+ },
+ const: {
+ type: "integer",
+ minimum: 0
+ }
+ }
+ }
+ ]
+ },
+ outerIIFEBody: {
+ type: "integer",
+ minimum: 0
+ },
+ MemberExpression: {
+ type: "integer",
+ minimum: 0
+ },
+ FunctionDeclaration: {
+ type: "object",
+ properties: {
+ parameters: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["first"]
+ }
+ ]
+ },
+ body: {
+ type: "integer",
+ minimum: 0
+ }
+ }
+ },
+ FunctionExpression: {
+ type: "object",
+ properties: {
+ parameters: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["first"]
+ }
+ ]
+ },
+ body: {
+ type: "integer",
+ minimum: 0
+ }
+ }
+ },
+ CallExpression: {
+ type: "object",
+ properties: {
+ parameters: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["first"]
+ }
+ ]
+ }
+ }
+ },
+ ArrayExpression: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["first"]
+ }
+ ]
+ },
+ ObjectExpression: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["first"]
+ }
+ ]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const DEFAULT_VARIABLE_INDENT = 1;
+ const DEFAULT_PARAMETER_INDENT = null; // For backwards compatibility, don't check parameter indentation unless specified in the config
+ const DEFAULT_FUNCTION_BODY_INDENT = 1;
+
+ let indentType = "space";
+ let indentSize = 4;
+ const options = {
+ SwitchCase: 0,
+ VariableDeclarator: {
+ var: DEFAULT_VARIABLE_INDENT,
+ let: DEFAULT_VARIABLE_INDENT,
+ const: DEFAULT_VARIABLE_INDENT
+ },
+ outerIIFEBody: null,
+ FunctionDeclaration: {
+ parameters: DEFAULT_PARAMETER_INDENT,
+ body: DEFAULT_FUNCTION_BODY_INDENT
+ },
+ FunctionExpression: {
+ parameters: DEFAULT_PARAMETER_INDENT,
+ body: DEFAULT_FUNCTION_BODY_INDENT
+ },
+ CallExpression: {
+ arguments: DEFAULT_PARAMETER_INDENT
+ },
+ ArrayExpression: 1,
+ ObjectExpression: 1
+ };
+
+ const sourceCode = context.getSourceCode();
+
+ if (context.options.length) {
+ if (context.options[0] === "tab") {
+ indentSize = 1;
+ indentType = "tab";
+ } else /* istanbul ignore else : this will be caught by options validation */ if (typeof context.options[0] === "number") {
+ indentSize = context.options[0];
+ indentType = "space";
+ }
+
+ if (context.options[1]) {
+ const opts = context.options[1];
+
+ options.SwitchCase = opts.SwitchCase || 0;
+ const variableDeclaratorRules = opts.VariableDeclarator;
+
+ if (typeof variableDeclaratorRules === "number") {
+ options.VariableDeclarator = {
+ var: variableDeclaratorRules,
+ let: variableDeclaratorRules,
+ const: variableDeclaratorRules
+ };
+ } else if (typeof variableDeclaratorRules === "object") {
+ Object.assign(options.VariableDeclarator, variableDeclaratorRules);
+ }
+
+ if (typeof opts.outerIIFEBody === "number") {
+ options.outerIIFEBody = opts.outerIIFEBody;
+ }
+
+ if (typeof opts.MemberExpression === "number") {
+ options.MemberExpression = opts.MemberExpression;
+ }
+
+ if (typeof opts.FunctionDeclaration === "object") {
+ Object.assign(options.FunctionDeclaration, opts.FunctionDeclaration);
+ }
+
+ if (typeof opts.FunctionExpression === "object") {
+ Object.assign(options.FunctionExpression, opts.FunctionExpression);
+ }
+
+ if (typeof opts.CallExpression === "object") {
+ Object.assign(options.CallExpression, opts.CallExpression);
+ }
+
+ if (typeof opts.ArrayExpression === "number" || typeof opts.ArrayExpression === "string") {
+ options.ArrayExpression = opts.ArrayExpression;
+ }
+
+ if (typeof opts.ObjectExpression === "number" || typeof opts.ObjectExpression === "string") {
+ options.ObjectExpression = opts.ObjectExpression;
+ }
+ }
+ }
+
+ const caseIndentStore = {};
+
+ /**
+ * Creates an error message for a line, given the expected/actual indentation.
+ * @param {int} expectedAmount The expected amount of indentation characters for this line
+ * @param {int} actualSpaces The actual number of indentation spaces that were found on this line
+ * @param {int} actualTabs The actual number of indentation tabs that were found on this line
+ * @returns {string} An error message for this line
+ */
+ function createErrorMessage(expectedAmount, actualSpaces, actualTabs) {
+ const expectedStatement = `${expectedAmount} ${indentType}${expectedAmount === 1 ? "" : "s"}`; // e.g. "2 tabs"
+ const foundSpacesWord = `space${actualSpaces === 1 ? "" : "s"}`; // e.g. "space"
+ const foundTabsWord = `tab${actualTabs === 1 ? "" : "s"}`; // e.g. "tabs"
+ let foundStatement;
+
+ if (actualSpaces > 0 && actualTabs > 0) {
+ foundStatement = `${actualSpaces} ${foundSpacesWord} and ${actualTabs} ${foundTabsWord}`; // e.g. "1 space and 2 tabs"
+ } else if (actualSpaces > 0) {
+
+ /*
+ * Abbreviate the message if the expected indentation is also spaces.
+ * e.g. 'Expected 4 spaces but found 2' rather than 'Expected 4 spaces but found 2 spaces'
+ */
+ foundStatement = indentType === "space" ? actualSpaces : `${actualSpaces} ${foundSpacesWord}`;
+ } else if (actualTabs > 0) {
+ foundStatement = indentType === "tab" ? actualTabs : `${actualTabs} ${foundTabsWord}`;
+ } else {
+ foundStatement = "0";
+ }
+
+ return `Expected indentation of ${expectedStatement} but found ${foundStatement}.`;
+ }
+
+ /**
+ * Reports a given indent violation
+ * @param {ASTNode} node Node violating the indent rule
+ * @param {int} needed Expected indentation character count
+ * @param {int} gottenSpaces Indentation space count in the actual node/code
+ * @param {int} gottenTabs Indentation tab count in the actual node/code
+ * @param {Object=} loc Error line and column location
+ * @param {boolean} isLastNodeCheck Is the error for last node check
+ * @param {int} lastNodeCheckEndOffset Number of charecters to skip from the end
+ * @returns {void}
+ */
+ function report(node, needed, gottenSpaces, gottenTabs, loc, isLastNodeCheck) {
+ if (gottenSpaces && gottenTabs) {
+
+ // To avoid conflicts with `no-mixed-spaces-and-tabs`, don't report lines that have both spaces and tabs.
+ return;
+ }
+
+ const desiredIndent = (indentType === "space" ? " " : "\t").repeat(needed);
+
+ const textRange = isLastNodeCheck
+ ? [node.range[1] - node.loc.end.column, node.range[1] - node.loc.end.column + gottenSpaces + gottenTabs]
+ : [node.range[0] - node.loc.start.column, node.range[0] - node.loc.start.column + gottenSpaces + gottenTabs];
+
+ context.report({
+ node,
+ loc,
+ message: createErrorMessage(needed, gottenSpaces, gottenTabs),
+ fix: fixer => fixer.replaceTextRange(textRange, desiredIndent)
+ });
+ }
+
+ /**
+ * Get the actual indent of node
+ * @param {ASTNode|Token} node Node to examine
+ * @param {boolean} [byLastLine=false] get indent of node's last line
+ * @returns {Object} The node's indent. Contains keys `space` and `tab`, representing the indent of each character. Also
+ * contains keys `goodChar` and `badChar`, where `goodChar` is the amount of the user's desired indentation character, and
+ * `badChar` is the amount of the other indentation character.
+ */
+ function getNodeIndent(node, byLastLine) {
+ const token = byLastLine ? sourceCode.getLastToken(node) : sourceCode.getFirstToken(node);
+ const srcCharsBeforeNode = sourceCode.getText(token, token.loc.start.column).split("");
+ const indentChars = srcCharsBeforeNode.slice(0, srcCharsBeforeNode.findIndex(char => char !== " " && char !== "\t"));
+ const spaces = indentChars.filter(char => char === " ").length;
+ const tabs = indentChars.filter(char => char === "\t").length;
+
+ return {
+ space: spaces,
+ tab: tabs,
+ goodChar: indentType === "space" ? spaces : tabs,
+ badChar: indentType === "space" ? tabs : spaces
+ };
+ }
+
+ /**
+ * Checks node is the first in its own start line. By default it looks by start line.
+ * @param {ASTNode} node The node to check
+ * @param {boolean} [byEndLocation=false] Lookup based on start position or end
+ * @returns {boolean} true if its the first in the its start line
+ */
+ function isNodeFirstInLine(node, byEndLocation) {
+ const firstToken = byEndLocation === true ? sourceCode.getLastToken(node, 1) : sourceCode.getTokenBefore(node),
+ startLine = byEndLocation === true ? node.loc.end.line : node.loc.start.line,
+ endLine = firstToken ? firstToken.loc.end.line : -1;
+
+ return startLine !== endLine;
+ }
+
+ /**
+ * Check indent for node
+ * @param {ASTNode} node Node to check
+ * @param {int} neededIndent needed indent
+ * @param {boolean} [excludeCommas=false] skip comma on start of line
+ * @returns {void}
+ */
+ function checkNodeIndent(node, neededIndent) {
+ const actualIndent = getNodeIndent(node, false);
+
+ if (
+ node.type !== "ArrayExpression" &&
+ node.type !== "ObjectExpression" &&
+ (actualIndent.goodChar !== neededIndent || actualIndent.badChar !== 0) &&
+ isNodeFirstInLine(node)
+ ) {
+ report(node, neededIndent, actualIndent.space, actualIndent.tab);
+ }
+
+ if (node.type === "IfStatement" && node.alternate) {
+ const elseToken = sourceCode.getTokenBefore(node.alternate);
+
+ checkNodeIndent(elseToken, neededIndent);
+
+ if (!isNodeFirstInLine(node.alternate)) {
+ checkNodeIndent(node.alternate, neededIndent);
+ }
+ }
+
+ if (node.type === "TryStatement" && node.handler) {
+ const catchToken = sourceCode.getFirstToken(node.handler);
+
+ checkNodeIndent(catchToken, neededIndent);
+ }
+
+ if (node.type === "TryStatement" && node.finalizer) {
+ const finallyToken = sourceCode.getTokenBefore(node.finalizer);
+
+ checkNodeIndent(finallyToken, neededIndent);
+ }
+
+ if (node.type === "DoWhileStatement") {
+ const whileToken = sourceCode.getTokenAfter(node.body);
+
+ checkNodeIndent(whileToken, neededIndent);
+ }
+ }
+
+ /**
+ * Check indent for nodes list
+ * @param {ASTNode[]} nodes list of node objects
+ * @param {int} indent needed indent
+ * @param {boolean} [excludeCommas=false] skip comma on start of line
+ * @returns {void}
+ */
+ function checkNodesIndent(nodes, indent) {
+ nodes.forEach(node => checkNodeIndent(node, indent));
+ }
+
+ /**
+ * Check last node line indent this detects, that block closed correctly
+ * @param {ASTNode} node Node to examine
+ * @param {int} lastLineIndent needed indent
+ * @returns {void}
+ */
+ function checkLastNodeLineIndent(node, lastLineIndent) {
+ const lastToken = sourceCode.getLastToken(node);
+ const endIndent = getNodeIndent(lastToken, true);
+
+ if ((endIndent.goodChar !== lastLineIndent || endIndent.badChar !== 0) && isNodeFirstInLine(node, true)) {
+ report(
+ node,
+ lastLineIndent,
+ endIndent.space,
+ endIndent.tab,
+ { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
+ true
+ );
+ }
+ }
+
+ /**
+ * Check last node line indent this detects, that block closed correctly
+ * This function for more complicated return statement case, where closing parenthesis may be followed by ';'
+ * @param {ASTNode} node Node to examine
+ * @param {int} firstLineIndent first line needed indent
+ * @returns {void}
+ */
+ function checkLastReturnStatementLineIndent(node, firstLineIndent) {
+
+ /*
+ * in case if return statement ends with ');' we have traverse back to ')'
+ * otherwise we'll measure indent for ';' and replace ')'
+ */
+ const lastToken = sourceCode.getLastToken(node, astUtils.isClosingParenToken);
+ const textBeforeClosingParenthesis = sourceCode.getText(lastToken, lastToken.loc.start.column).slice(0, -1);
+
+ if (textBeforeClosingParenthesis.trim()) {
+
+ // There are tokens before the closing paren, don't report this case
+ return;
+ }
+
+ const endIndent = getNodeIndent(lastToken, true);
+
+ if (endIndent.goodChar !== firstLineIndent) {
+ report(
+ node,
+ firstLineIndent,
+ endIndent.space,
+ endIndent.tab,
+ { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
+ true
+ );
+ }
+ }
+
+ /**
+ * Check first node line indent is correct
+ * @param {ASTNode} node Node to examine
+ * @param {int} firstLineIndent needed indent
+ * @returns {void}
+ */
+ function checkFirstNodeLineIndent(node, firstLineIndent) {
+ const startIndent = getNodeIndent(node, false);
+
+ if ((startIndent.goodChar !== firstLineIndent || startIndent.badChar !== 0) && isNodeFirstInLine(node)) {
+ report(
+ node,
+ firstLineIndent,
+ startIndent.space,
+ startIndent.tab,
+ { line: node.loc.start.line, column: node.loc.start.column }
+ );
+ }
+ }
+
+ /**
+ * Returns a parent node of given node based on a specified type
+ * if not present then return null
+ * @param {ASTNode} node node to examine
+ * @param {string} type type that is being looked for
+ * @param {string} stopAtList end points for the evaluating code
+ * @returns {ASTNode|void} if found then node otherwise null
+ */
+ function getParentNodeByType(node, type, stopAtList) {
+ let parent = node.parent;
+
+ if (!stopAtList) {
+ stopAtList = ["Program"];
+ }
+
+ while (parent.type !== type && stopAtList.indexOf(parent.type) === -1 && parent.type !== "Program") {
+ parent = parent.parent;
+ }
+
+ return parent.type === type ? parent : null;
+ }
+
+ /**
+ * Returns the VariableDeclarator based on the current node
+ * if not present then return null
+ * @param {ASTNode} node node to examine
+ * @returns {ASTNode|void} if found then node otherwise null
+ */
+ function getVariableDeclaratorNode(node) {
+ return getParentNodeByType(node, "VariableDeclarator");
+ }
+
+ /**
+ * Check to see if the node is part of the multi-line variable declaration.
+ * Also if its on the same line as the varNode
+ * @param {ASTNode} node node to check
+ * @param {ASTNode} varNode variable declaration node to check against
+ * @returns {boolean} True if all the above condition satisfy
+ */
+ function isNodeInVarOnTop(node, varNode) {
+ return varNode &&
+ varNode.parent.loc.start.line === node.loc.start.line &&
+ varNode.parent.declarations.length > 1;
+ }
+
+ /**
+ * Check to see if the argument before the callee node is multi-line and
+ * there should only be 1 argument before the callee node
+ * @param {ASTNode} node node to check
+ * @returns {boolean} True if arguments are multi-line
+ */
+ function isArgBeforeCalleeNodeMultiline(node) {
+ const parent = node.parent;
+
+ if (parent.arguments.length >= 2 && parent.arguments[1] === node) {
+ return parent.arguments[0].loc.end.line > parent.arguments[0].loc.start.line;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check to see if the node is a file level IIFE
+ * @param {ASTNode} node The function node to check.
+ * @returns {boolean} True if the node is the outer IIFE
+ */
+ function isOuterIIFE(node) {
+ const parent = node.parent;
+ let stmt = parent.parent;
+
+ /*
+ * Verify that the node is an IIEF
+ */
+ if (
+ parent.type !== "CallExpression" ||
+ parent.callee !== node) {
+
+ return false;
+ }
+
+ /*
+ * Navigate legal ancestors to determine whether this IIEF is outer
+ */
+ while (
+ stmt.type === "UnaryExpression" && (
+ stmt.operator === "!" ||
+ stmt.operator === "~" ||
+ stmt.operator === "+" ||
+ stmt.operator === "-") ||
+ stmt.type === "AssignmentExpression" ||
+ stmt.type === "LogicalExpression" ||
+ stmt.type === "SequenceExpression" ||
+ stmt.type === "VariableDeclarator") {
+
+ stmt = stmt.parent;
+ }
+
+ return ((
+ stmt.type === "ExpressionStatement" ||
+ stmt.type === "VariableDeclaration") &&
+ stmt.parent && stmt.parent.type === "Program"
+ );
+ }
+
+ /**
+ * Check indent for function block content
+ * @param {ASTNode} node A BlockStatement node that is inside of a function.
+ * @returns {void}
+ */
+ function checkIndentInFunctionBlock(node) {
+
+ /*
+ * Search first caller in chain.
+ * Ex.:
+ *
+ * Models <- Identifier
+ * .User
+ * .find()
+ * .exec(function() {
+ * // function body
+ * });
+ *
+ * Looks for 'Models'
+ */
+ const calleeNode = node.parent; // FunctionExpression
+ let indent;
+
+ if (calleeNode.parent &&
+ (calleeNode.parent.type === "Property" ||
+ calleeNode.parent.type === "ArrayExpression")) {
+
+ // If function is part of array or object, comma can be put at left
+ indent = getNodeIndent(calleeNode, false).goodChar;
+ } else {
+
+ // If function is standalone, simple calculate indent
+ indent = getNodeIndent(calleeNode).goodChar;
+ }
+
+ if (calleeNode.parent.type === "CallExpression") {
+ const calleeParent = calleeNode.parent;
+
+ if (calleeNode.type !== "FunctionExpression" && calleeNode.type !== "ArrowFunctionExpression") {
+ if (calleeParent && calleeParent.loc.start.line < node.loc.start.line) {
+ indent = getNodeIndent(calleeParent).goodChar;
+ }
+ } else {
+ if (isArgBeforeCalleeNodeMultiline(calleeNode) &&
+ calleeParent.callee.loc.start.line === calleeParent.callee.loc.end.line &&
+ !isNodeFirstInLine(calleeNode)) {
+ indent = getNodeIndent(calleeParent).goodChar;
+ }
+ }
+ }
+
+ /*
+ * function body indent should be indent + indent size, unless this
+ * is a FunctionDeclaration, FunctionExpression, or outer IIFE and the corresponding options are enabled.
+ */
+ let functionOffset = indentSize;
+
+ if (options.outerIIFEBody !== null && isOuterIIFE(calleeNode)) {
+ functionOffset = options.outerIIFEBody * indentSize;
+ } else if (calleeNode.type === "FunctionExpression") {
+ functionOffset = options.FunctionExpression.body * indentSize;
+ } else if (calleeNode.type === "FunctionDeclaration") {
+ functionOffset = options.FunctionDeclaration.body * indentSize;
+ }
+ indent += functionOffset;
+
+ // check if the node is inside a variable
+ const parentVarNode = getVariableDeclaratorNode(node);
+
+ if (parentVarNode && isNodeInVarOnTop(node, parentVarNode)) {
+ indent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
+ }
+
+ if (node.body.length > 0) {
+ checkNodesIndent(node.body, indent);
+ }
+
+ checkLastNodeLineIndent(node, indent - functionOffset);
+ }
+
+
+ /**
+ * Checks if the given node starts and ends on the same line
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} Whether or not the block starts and ends on the same line.
+ */
+ function isSingleLineNode(node) {
+ const lastToken = sourceCode.getLastToken(node),
+ startLine = node.loc.start.line,
+ endLine = lastToken.loc.end.line;
+
+ return startLine === endLine;
+ }
+
+ /**
+ * Check to see if the first element inside an array is an object and on the same line as the node
+ * If the node is not an array then it will return false.
+ * @param {ASTNode} node node to check
+ * @returns {boolean} success/failure
+ */
+ function isFirstArrayElementOnSameLine(node) {
+ if (node.type === "ArrayExpression" && node.elements[0]) {
+ return node.elements[0].loc.start.line === node.loc.start.line && node.elements[0].type === "ObjectExpression";
+ }
+ return false;
+
+ }
+
+ /**
+ * Check indent for array block content or object block content
+ * @param {ASTNode} node node to examine
+ * @returns {void}
+ */
+ function checkIndentInArrayOrObjectBlock(node) {
+
+ // Skip inline
+ if (isSingleLineNode(node)) {
+ return;
+ }
+
+ let elements = (node.type === "ArrayExpression") ? node.elements : node.properties;
+
+ // filter out empty elements example would be [ , 2] so remove first element as espree considers it as null
+ elements = elements.filter(elem => elem !== null);
+
+ let nodeIndent;
+ let elementsIndent;
+ const parentVarNode = getVariableDeclaratorNode(node);
+
+ // TODO - come up with a better strategy in future
+ if (isNodeFirstInLine(node)) {
+ const parent = node.parent;
+
+ nodeIndent = getNodeIndent(parent).goodChar;
+ if (!parentVarNode || parentVarNode.loc.start.line !== node.loc.start.line) {
+ if (parent.type !== "VariableDeclarator" || parentVarNode === parentVarNode.parent.declarations[0]) {
+ if (parent.type === "VariableDeclarator" && parentVarNode.loc.start.line === parent.loc.start.line) {
+ nodeIndent += (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]);
+ } else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
+ const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;
+
+ if (parentElements[0] &&
+ parentElements[0].loc.start.line === parent.loc.start.line &&
+ parentElements[0].loc.end.line !== parent.loc.start.line) {
+
+ /*
+ * If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
+ * e.g. [{
+ * foo: 1
+ * },
+ * {
+ * bar: 1
+ * }]
+ * the second object is not indented.
+ */
+ } else if (typeof options[parent.type] === "number") {
+ nodeIndent += options[parent.type] * indentSize;
+ } else {
+ nodeIndent = parentElements[0].loc.start.column;
+ }
+ } else if (parent.type === "CallExpression" || parent.type === "NewExpression") {
+ if (typeof options.CallExpression.arguments === "number") {
+ nodeIndent += options.CallExpression.arguments * indentSize;
+ } else if (options.CallExpression.arguments === "first") {
+ if (parent.arguments.indexOf(node) !== -1) {
+ nodeIndent = parent.arguments[0].loc.start.column;
+ }
+ } else {
+ nodeIndent += indentSize;
+ }
+ } else if (parent.type === "LogicalExpression" || parent.type === "ArrowFunctionExpression") {
+ nodeIndent += indentSize;
+ }
+ }
+ } else if (!parentVarNode && !isFirstArrayElementOnSameLine(parent) && parent.type !== "MemberExpression" && parent.type !== "ExpressionStatement" && parent.type !== "AssignmentExpression" && parent.type !== "Property") {
+ nodeIndent += indentSize;
+ }
+
+ checkFirstNodeLineIndent(node, nodeIndent);
+ } else {
+ nodeIndent = getNodeIndent(node).goodChar;
+ }
+
+ if (options[node.type] === "first") {
+ elementsIndent = elements.length ? elements[0].loc.start.column : 0; // If there are no elements, elementsIndent doesn't matter.
+ } else {
+ elementsIndent = nodeIndent + indentSize * options[node.type];
+ }
+
+ /*
+ * Check if the node is a multiple variable declaration; if so, then
+ * make sure indentation takes that into account.
+ */
+ if (isNodeInVarOnTop(node, parentVarNode)) {
+ elementsIndent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
+ }
+
+ checkNodesIndent(elements, elementsIndent);
+
+ if (elements.length > 0) {
+
+ // Skip last block line check if last item in same line
+ if (elements[elements.length - 1].loc.end.line === node.loc.end.line) {
+ return;
+ }
+ }
+
+ checkLastNodeLineIndent(node, nodeIndent +
+ (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
+ }
+
+ /**
+ * Check if the node or node body is a BlockStatement or not
+ * @param {ASTNode} node node to test
+ * @returns {boolean} True if it or its body is a block statement
+ */
+ function isNodeBodyBlock(node) {
+ return node.type === "BlockStatement" || node.type === "ClassBody" || (node.body && node.body.type === "BlockStatement") ||
+ (node.consequent && node.consequent.type === "BlockStatement");
+ }
+
+ /**
+ * Check indentation for blocks
+ * @param {ASTNode} node node to check
+ * @returns {void}
+ */
+ function blockIndentationCheck(node) {
+
+ // Skip inline blocks
+ if (isSingleLineNode(node)) {
+ return;
+ }
+
+ if (node.parent && (
+ node.parent.type === "FunctionExpression" ||
+ node.parent.type === "FunctionDeclaration" ||
+ node.parent.type === "ArrowFunctionExpression")
+ ) {
+ checkIndentInFunctionBlock(node);
+ return;
+ }
+
+ let indent;
+ let nodesToCheck = [];
+
+ /*
+ * For this statements we should check indent from statement beginning,
+ * not from the beginning of the block.
+ */
+ const statementsWithProperties = [
+ "IfStatement", "WhileStatement", "ForStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement", "ClassDeclaration", "TryStatement"
+ ];
+
+ if (node.parent && statementsWithProperties.indexOf(node.parent.type) !== -1 && isNodeBodyBlock(node)) {
+ indent = getNodeIndent(node.parent).goodChar;
+ } else if (node.parent && node.parent.type === "CatchClause") {
+ indent = getNodeIndent(node.parent.parent).goodChar;
+ } else {
+ indent = getNodeIndent(node).goodChar;
+ }
+
+ if (node.type === "IfStatement" && node.consequent.type !== "BlockStatement") {
+ nodesToCheck = [node.consequent];
+ } else if (Array.isArray(node.body)) {
+ nodesToCheck = node.body;
+ } else {
+ nodesToCheck = [node.body];
+ }
+
+ if (nodesToCheck.length > 0) {
+ checkNodesIndent(nodesToCheck, indent + indentSize);
+ }
+
+ if (node.type === "BlockStatement") {
+ checkLastNodeLineIndent(node, indent);
+ }
+ }
+
+ /**
+ * Filter out the elements which are on the same line of each other or the node.
+ * basically have only 1 elements from each line except the variable declaration line.
+ * @param {ASTNode} node Variable declaration node
+ * @returns {ASTNode[]} Filtered elements
+ */
+ function filterOutSameLineVars(node) {
+ return node.declarations.reduce((finalCollection, elem) => {
+ const lastElem = finalCollection[finalCollection.length - 1];
+
+ if ((elem.loc.start.line !== node.loc.start.line && !lastElem) ||
+ (lastElem && lastElem.loc.start.line !== elem.loc.start.line)) {
+ finalCollection.push(elem);
+ }
+
+ return finalCollection;
+ }, []);
+ }
+
+ /**
+ * Check indentation for variable declarations
+ * @param {ASTNode} node node to examine
+ * @returns {void}
+ */
+ function checkIndentInVariableDeclarations(node) {
+ const elements = filterOutSameLineVars(node);
+ const nodeIndent = getNodeIndent(node).goodChar;
+ const lastElement = elements[elements.length - 1];
+
+ const elementsIndent = nodeIndent + indentSize * options.VariableDeclarator[node.kind];
+
+ checkNodesIndent(elements, elementsIndent);
+
+ // Only check the last line if there is any token after the last item
+ if (sourceCode.getLastToken(node).loc.end.line <= lastElement.loc.end.line) {
+ return;
+ }
+
+ const tokenBeforeLastElement = sourceCode.getTokenBefore(lastElement);
+
+ if (tokenBeforeLastElement.value === ",") {
+
+ // Special case for comma-first syntax where the semicolon is indented
+ checkLastNodeLineIndent(node, getNodeIndent(tokenBeforeLastElement).goodChar);
+ } else {
+ checkLastNodeLineIndent(node, elementsIndent - indentSize);
+ }
+ }
+
+ /**
+ * Check and decide whether to check for indentation for blockless nodes
+ * Scenarios are for or while statements without braces around them
+ * @param {ASTNode} node node to examine
+ * @returns {void}
+ */
+ function blockLessNodes(node) {
+ if (node.body.type !== "BlockStatement") {
+ blockIndentationCheck(node);
+ }
+ }
+
+ /**
+ * Returns the expected indentation for the case statement
+ * @param {ASTNode} node node to examine
+ * @param {int} [switchIndent] indent for switch statement
+ * @returns {int} indent size
+ */
+ function expectedCaseIndent(node, switchIndent) {
+ const switchNode = (node.type === "SwitchStatement") ? node : node.parent;
+ let caseIndent;
+
+ if (caseIndentStore[switchNode.loc.start.line]) {
+ return caseIndentStore[switchNode.loc.start.line];
+ }
+ if (typeof switchIndent === "undefined") {
+ switchIndent = getNodeIndent(switchNode).goodChar;
+ }
+
+ if (switchNode.cases.length > 0 && options.SwitchCase === 0) {
+ caseIndent = switchIndent;
+ } else {
+ caseIndent = switchIndent + (indentSize * options.SwitchCase);
+ }
+
+ caseIndentStore[switchNode.loc.start.line] = caseIndent;
+ return caseIndent;
+
+ }
+
+ /**
+ * Checks wether a return statement is wrapped in ()
+ * @param {ASTNode} node node to examine
+ * @returns {boolean} the result
+ */
+ function isWrappedInParenthesis(node) {
+ const regex = /^return\s*?\(\s*?\);*?/;
+
+ const statementWithoutArgument = sourceCode.getText(node).replace(
+ sourceCode.getText(node.argument), ""
+ );
+
+ return regex.test(statementWithoutArgument);
+ }
+
+ return {
+ Program(node) {
+ if (node.body.length > 0) {
+
+ // Root nodes should have no indent
+ checkNodesIndent(node.body, getNodeIndent(node).goodChar);
+ }
+ },
+
+ ClassBody: blockIndentationCheck,
+
+ BlockStatement: blockIndentationCheck,
+
+ WhileStatement: blockLessNodes,
+
+ ForStatement: blockLessNodes,
+
+ ForInStatement: blockLessNodes,
+
+ ForOfStatement: blockLessNodes,
+
+ DoWhileStatement: blockLessNodes,
+
+ IfStatement(node) {
+ if (node.consequent.type !== "BlockStatement" && node.consequent.loc.start.line > node.loc.start.line) {
+ blockIndentationCheck(node);
+ }
+ },
+
+ VariableDeclaration(node) {
+ if (node.declarations[node.declarations.length - 1].loc.start.line > node.declarations[0].loc.start.line) {
+ checkIndentInVariableDeclarations(node);
+ }
+ },
+
+ ObjectExpression(node) {
+ checkIndentInArrayOrObjectBlock(node);
+ },
+
+ ArrayExpression(node) {
+ checkIndentInArrayOrObjectBlock(node);
+ },
+
+ MemberExpression(node) {
+
+ if (typeof options.MemberExpression === "undefined") {
+ return;
+ }
+
+ if (isSingleLineNode(node)) {
+ return;
+ }
+
+ /*
+ * The typical layout of variable declarations and assignments
+ * alter the expectation of correct indentation. Skip them.
+ * TODO: Add appropriate configuration options for variable
+ * declarations and assignments.
+ */
+ if (getParentNodeByType(node, "VariableDeclarator", ["FunctionExpression", "ArrowFunctionExpression"])) {
+ return;
+ }
+
+ if (getParentNodeByType(node, "AssignmentExpression", ["FunctionExpression"])) {
+ return;
+ }
+
+ const propertyIndent = getNodeIndent(node).goodChar + indentSize * options.MemberExpression;
+
+ const checkNodes = [node.property];
+
+ const dot = sourceCode.getTokenBefore(node.property);
+
+ if (dot.type === "Punctuator" && dot.value === ".") {
+ checkNodes.push(dot);
+ }
+
+ checkNodesIndent(checkNodes, propertyIndent);
+ },
+
+ SwitchStatement(node) {
+
+ // Switch is not a 'BlockStatement'
+ const switchIndent = getNodeIndent(node).goodChar;
+ const caseIndent = expectedCaseIndent(node, switchIndent);
+
+ checkNodesIndent(node.cases, caseIndent);
+
+
+ checkLastNodeLineIndent(node, switchIndent);
+ },
+
+ SwitchCase(node) {
+
+ // Skip inline cases
+ if (isSingleLineNode(node)) {
+ return;
+ }
+ const caseIndent = expectedCaseIndent(node);
+
+ checkNodesIndent(node.consequent, caseIndent + indentSize);
+ },
+
+ FunctionDeclaration(node) {
+ if (isSingleLineNode(node)) {
+ return;
+ }
+ if (options.FunctionDeclaration.parameters === "first" && node.params.length) {
+ checkNodesIndent(node.params.slice(1), node.params[0].loc.start.column);
+ } else if (options.FunctionDeclaration.parameters !== null) {
+ checkNodesIndent(node.params, getNodeIndent(node).goodChar + indentSize * options.FunctionDeclaration.parameters);
+ }
+ },
+
+ FunctionExpression(node) {
+ if (isSingleLineNode(node)) {
+ return;
+ }
+ if (options.FunctionExpression.parameters === "first" && node.params.length) {
+ checkNodesIndent(node.params.slice(1), node.params[0].loc.start.column);
+ } else if (options.FunctionExpression.parameters !== null) {
+ checkNodesIndent(node.params, getNodeIndent(node).goodChar + indentSize * options.FunctionExpression.parameters);
+ }
+ },
+
+ ReturnStatement(node) {
+ if (isSingleLineNode(node)) {
+ return;
+ }
+
+ const firstLineIndent = getNodeIndent(node).goodChar;
+
+ // in case if return statement is wrapped in parenthesis
+ if (isWrappedInParenthesis(node)) {
+ checkLastReturnStatementLineIndent(node, firstLineIndent);
+ } else {
+ checkNodeIndent(node, firstLineIndent);
+ }
+ },
+
+ CallExpression(node) {
+ if (isSingleLineNode(node)) {
+ return;
+ }
+ if (options.CallExpression.arguments === "first" && node.arguments.length) {
+ checkNodesIndent(node.arguments.slice(1), node.arguments[0].loc.start.column);
+ } else if (options.CallExpression.arguments !== null) {
+ checkNodesIndent(node.arguments, getNodeIndent(node).goodChar + indentSize * options.CallExpression.arguments);
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/indent.js b/tools/node_modules/eslint/lib/rules/indent.js
new file mode 100644
index 0000000000..42cebf9ea5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/indent.js
@@ -0,0 +1,1522 @@
+/**
+ * @fileoverview This option sets a specific tab width for your code
+ *
+ * @author Teddy Katz
+ * @author Vitaly Puzrin
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+const astUtils = require("../ast-utils");
+const createTree = require("functional-red-black-tree");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const KNOWN_NODES = new Set([
+ "AssignmentExpression",
+ "AssignmentPattern",
+ "ArrayExpression",
+ "ArrayPattern",
+ "ArrowFunctionExpression",
+ "AwaitExpression",
+ "BlockStatement",
+ "BinaryExpression",
+ "BreakStatement",
+ "CallExpression",
+ "CatchClause",
+ "ClassBody",
+ "ClassDeclaration",
+ "ClassExpression",
+ "ConditionalExpression",
+ "ContinueStatement",
+ "DoWhileStatement",
+ "DebuggerStatement",
+ "EmptyStatement",
+ "ExperimentalRestProperty",
+ "ExperimentalSpreadProperty",
+ "ExpressionStatement",
+ "ForStatement",
+ "ForInStatement",
+ "ForOfStatement",
+ "FunctionDeclaration",
+ "FunctionExpression",
+ "Identifier",
+ "IfStatement",
+ "Literal",
+ "LabeledStatement",
+ "LogicalExpression",
+ "MemberExpression",
+ "MetaProperty",
+ "MethodDefinition",
+ "NewExpression",
+ "ObjectExpression",
+ "ObjectPattern",
+ "Program",
+ "Property",
+ "RestElement",
+ "ReturnStatement",
+ "SequenceExpression",
+ "SpreadElement",
+ "Super",
+ "SwitchCase",
+ "SwitchStatement",
+ "TaggedTemplateExpression",
+ "TemplateElement",
+ "TemplateLiteral",
+ "ThisExpression",
+ "ThrowStatement",
+ "TryStatement",
+ "UnaryExpression",
+ "UpdateExpression",
+ "VariableDeclaration",
+ "VariableDeclarator",
+ "WhileStatement",
+ "WithStatement",
+ "YieldExpression",
+ "JSXIdentifier",
+ "JSXNamespacedName",
+ "JSXMemberExpression",
+ "JSXEmptyExpression",
+ "JSXExpressionContainer",
+ "JSXElement",
+ "JSXClosingElement",
+ "JSXOpeningElement",
+ "JSXAttribute",
+ "JSXSpreadAttribute",
+ "JSXText",
+ "ExportDefaultDeclaration",
+ "ExportNamedDeclaration",
+ "ExportAllDeclaration",
+ "ExportSpecifier",
+ "ImportDeclaration",
+ "ImportSpecifier",
+ "ImportDefaultSpecifier",
+ "ImportNamespaceSpecifier"
+]);
+
+/*
+ * General rule strategy:
+ * 1. An OffsetStorage instance stores a map of desired offsets, where each token has a specified offset from another
+ * specified token or to the first column.
+ * 2. As the AST is traversed, modify the desired offsets of tokens accordingly. For example, when entering a
+ * BlockStatement, offset all of the tokens in the BlockStatement by 1 indent level from the opening curly
+ * brace of the BlockStatement.
+ * 3. After traversing the AST, calculate the expected indentation levels of every token according to the
+ * OffsetStorage container.
+ * 4. For each line, compare the expected indentation of the first token to the actual indentation in the file,
+ * and report the token if the two values are not equal.
+ */
+
+
+/**
+ * A mutable balanced binary search tree that stores (key, value) pairs. The keys are numeric, and must be unique.
+ * This is intended to be a generic wrapper around a balanced binary search tree library, so that the underlying implementation
+ * can easily be swapped out.
+ */
+class BinarySearchTree {
+
+ /**
+ * Creates an empty tree
+ */
+ constructor() {
+ this._rbTree = createTree();
+ }
+
+ /**
+ * Inserts an entry into the tree.
+ * @param {number} key The entry's key
+ * @param {*} value The entry's value
+ * @returns {void}
+ */
+ insert(key, value) {
+ const iterator = this._rbTree.find(key);
+
+ if (iterator.valid) {
+ this._rbTree = iterator.update(value);
+ } else {
+ this._rbTree = this._rbTree.insert(key, value);
+ }
+ }
+
+ /**
+ * Finds the entry with the largest key less than or equal to the provided key
+ * @param {number} key The provided key
+ * @returns {{key: number, value: *}|null} The found entry, or null if no such entry exists.
+ */
+ findLe(key) {
+ const iterator = this._rbTree.le(key);
+
+ return iterator && { key: iterator.key, value: iterator.value };
+ }
+
+ /**
+ * Deletes all of the keys in the interval [start, end)
+ * @param {number} start The start of the range
+ * @param {number} end The end of the range
+ * @returns {void}
+ */
+ deleteRange(start, end) {
+
+ // Exit without traversing the tree if the range has zero size.
+ if (start === end) {
+ return;
+ }
+ const iterator = this._rbTree.ge(start);
+
+ while (iterator.valid && iterator.key < end) {
+ this._rbTree = this._rbTree.remove(iterator.key);
+ iterator.next();
+ }
+ }
+}
+
+/**
+ * A helper class to get token-based info related to indentation
+ */
+class TokenInfo {
+
+ /**
+ * @param {SourceCode} sourceCode A SourceCode object
+ */
+ constructor(sourceCode) {
+ this.sourceCode = sourceCode;
+ this.firstTokensByLineNumber = sourceCode.tokensAndComments.reduce((map, token) => {
+ if (!map.has(token.loc.start.line)) {
+ map.set(token.loc.start.line, token);
+ }
+ if (!map.has(token.loc.end.line) && sourceCode.text.slice(token.range[1] - token.loc.end.column, token.range[1]).trim()) {
+ map.set(token.loc.end.line, token);
+ }
+ return map;
+ }, new Map());
+ }
+
+ /**
+ * Gets the first token on a given token's line
+ * @param {Token|ASTNode} token a node or token
+ * @returns {Token} The first token on the given line
+ */
+ getFirstTokenOfLine(token) {
+ return this.firstTokensByLineNumber.get(token.loc.start.line);
+ }
+
+ /**
+ * Determines whether a token is the first token in its line
+ * @param {Token} token The token
+ * @returns {boolean} `true` if the token is the first on its line
+ */
+ isFirstTokenOfLine(token) {
+ return this.getFirstTokenOfLine(token) === token;
+ }
+
+ /**
+ * Get the actual indent of a token
+ * @param {Token} token Token to examine. This should be the first token on its line.
+ * @returns {string} The indentation characters that precede the token
+ */
+ getTokenIndent(token) {
+ return this.sourceCode.text.slice(token.range[0] - token.loc.start.column, token.range[0]);
+ }
+}
+
+/**
+ * A class to store information on desired offsets of tokens from each other
+ */
+class OffsetStorage {
+
+ /**
+ * @param {TokenInfo} tokenInfo a TokenInfo instance
+ * @param {number} indentSize The desired size of each indentation level
+ * @param {string} indentType The indentation character
+ */
+ constructor(tokenInfo, indentSize, indentType) {
+ this._tokenInfo = tokenInfo;
+ this._indentSize = indentSize;
+ this._indentType = indentType;
+
+ this._tree = new BinarySearchTree();
+ this._tree.insert(0, { offset: 0, from: null, force: false });
+
+ this._lockedFirstTokens = new WeakMap();
+ this._desiredIndentCache = new WeakMap();
+ this._ignoredTokens = new WeakSet();
+ }
+
+ _getOffsetDescriptor(token) {
+ return this._tree.findLe(token.range[0]).value;
+ }
+
+ /**
+ * Sets the offset column of token B to match the offset column of token A.
+ * **WARNING**: This matches a *column*, even if baseToken is not the first token on its line. In
+ * most cases, `setDesiredOffset` should be used instead.
+ * @param {Token} baseToken The first token
+ * @param {Token} offsetToken The second token, whose offset should be matched to the first token
+ * @returns {void}
+ */
+ matchOffsetOf(baseToken, offsetToken) {
+
+ /*
+ * lockedFirstTokens is a map from a token whose indentation is controlled by the "first" option to
+ * the token that it depends on. For example, with the `ArrayExpression: first` option, the first
+ * token of each element in the array after the first will be mapped to the first token of the first
+ * element. The desired indentation of each of these tokens is computed based on the desired indentation
+ * of the "first" element, rather than through the normal offset mechanism.
+ */
+ this._lockedFirstTokens.set(offsetToken, baseToken);
+ }
+
+ /**
+ * Sets the desired offset of a token.
+ *
+ * This uses a line-based offset collapsing behavior to handle tokens on the same line.
+ * For example, consider the following two cases:
+ *
+ * (
+ * [
+ * bar
+ * ]
+ * )
+ *
+ * ([
+ * bar
+ * ])
+ *
+ * Based on the first case, it's clear that the `bar` token needs to have an offset of 1 indent level (4 spaces) from
+ * the `[` token, and the `[` token has to have an offset of 1 indent level from the `(` token. Since the `(` token is
+ * the first on its line (with an indent of 0 spaces), the `bar` token needs to be offset by 2 indent levels (8 spaces)
+ * from the start of its line.
+ *
+ * However, in the second case `bar` should only be indented by 4 spaces. This is because the offset of 1 indent level
+ * between the `(` and the `[` tokens gets "collapsed" because the two tokens are on the same line. As a result, the
+ * `(` token is mapped to the `[` token with an offset of 0, and the rule correctly decides that `bar` should be indented
+ * by 1 indent level from the start of the line.
+ *
+ * This is useful because rule listeners can usually just call `setDesiredOffset` for all the tokens in the node,
+ * without needing to check which lines those tokens are on.
+ *
+ * Note that since collapsing only occurs when two tokens are on the same line, there are a few cases where non-intuitive
+ * behavior can occur. For example, consider the following cases:
+ *
+ * foo(
+ * ).
+ * bar(
+ * baz
+ * )
+ *
+ * foo(
+ * ).bar(
+ * baz
+ * )
+ *
+ * Based on the first example, it would seem that `bar` should be offset by 1 indent level from `foo`, and `baz`
+ * should be offset by 1 indent level from `bar`. However, this is not correct, because it would result in `baz`
+ * being indented by 2 indent levels in the second case (since `foo`, `bar`, and `baz` are all on separate lines, no
+ * collapsing would occur).
+ *
+ * Instead, the correct way would be to offset `baz` by 1 level from `bar`, offset `bar` by 1 level from the `)`, and
+ * offset the `)` by 0 levels from `foo`. This ensures that the offset between `bar` and the `)` are correctly collapsed
+ * in the second case.
+ *
+ * @param {Token} token The token
+ * @param {Token} fromToken The token that `token` should be offset from
+ * @param {number} offset The desired indent level
+ * @returns {void}
+ */
+ setDesiredOffset(token, fromToken, offset) {
+ return this.setDesiredOffsets(token.range, fromToken, offset);
+ }
+
+ /**
+ * Sets the desired offset of all tokens in a range
+ * It's common for node listeners in this file to need to apply the same offset to a large, contiguous range of tokens.
+ * Moreover, the offset of any given token is usually updated multiple times (roughly once for each node that contains
+ * it). This means that the offset of each token is updated O(AST depth) times.
+ * It would not be performant to store and update the offsets for each token independently, because the rule would end
+ * up having a time complexity of O(number of tokens * AST depth), which is quite slow for large files.
+ *
+ * Instead, the offset tree is represented as a collection of contiguous offset ranges in a file. For example, the following
+ * list could represent the state of the offset tree at a given point:
+ *
+ * * Tokens starting in the interval [0, 15) are aligned with the beginning of the file
+ * * Tokens starting in the interval [15, 30) are offset by 1 indent level from the `bar` token
+ * * Tokens starting in the interval [30, 43) are offset by 1 indent level from the `foo` token
+ * * Tokens starting in the interval [43, 820) are offset by 2 indent levels from the `bar` token
+ * * Tokens starting in the interval [820, ∞) are offset by 1 indent level from the `baz` token
+ *
+ * The `setDesiredOffsets` methods inserts ranges like the ones above. The third line above would be inserted by using:
+ * `setDesiredOffsets([30, 43], fooToken, 1);`
+ *
+ * @param {[number, number]} range A [start, end] pair. All tokens with range[0] <= token.start < range[1] will have the offset applied.
+ * @param {Token} fromToken The token that this is offset from
+ * @param {number} offset The desired indent level
+ * @param {boolean} force `true` if this offset should not use the normal collapsing behavior. This should almost always be false.
+ * @returns {void}
+ */
+ setDesiredOffsets(range, fromToken, offset, force) {
+
+ /*
+ * Offset ranges are stored as a collection of nodes, where each node maps a numeric key to an offset
+ * descriptor. The tree for the example above would have the following nodes:
+ *
+ * * key: 0, value: { offset: 0, from: null }
+ * * key: 15, value: { offset: 1, from: barToken }
+ * * key: 30, value: { offset: 1, from: fooToken }
+ * * key: 43, value: { offset: 2, from: barToken }
+ * * key: 820, value: { offset: 1, from: bazToken }
+ *
+ * To find the offset descriptor for any given token, one needs to find the node with the largest key
+ * which is <= token.start. To make this operation fast, the nodes are stored in a balanced binary
+ * search tree indexed by key.
+ */
+
+ const descriptorToInsert = { offset, from: fromToken, force };
+
+ const descriptorAfterRange = this._tree.findLe(range[1]).value;
+
+ const fromTokenIsInRange = fromToken && fromToken.range[0] >= range[0] && fromToken.range[1] <= range[1];
+ const fromTokenDescriptor = fromTokenIsInRange && this._getOffsetDescriptor(fromToken);
+
+ // First, remove any existing nodes in the range from the tree.
+ this._tree.deleteRange(range[0] + 1, range[1]);
+
+ // Insert a new node into the tree for this range
+ this._tree.insert(range[0], descriptorToInsert);
+
+ /*
+ * To avoid circular offset dependencies, keep the `fromToken` token mapped to whatever it was mapped to previously,
+ * even if it's in the current range.
+ */
+ if (fromTokenIsInRange) {
+ this._tree.insert(fromToken.range[0], fromTokenDescriptor);
+ this._tree.insert(fromToken.range[1], descriptorToInsert);
+ }
+
+ /*
+ * To avoid modifying the offset of tokens after the range, insert another node to keep the offset of the following
+ * tokens the same as it was before.
+ */
+ this._tree.insert(range[1], descriptorAfterRange);
+ }
+
+ /**
+ * Gets the desired indent of a token
+ * @param {Token} token The token
+ * @returns {string} The desired indent of the token
+ */
+ getDesiredIndent(token) {
+ if (!this._desiredIndentCache.has(token)) {
+
+ if (this._ignoredTokens.has(token)) {
+
+ /*
+ * If the token is ignored, use the actual indent of the token as the desired indent.
+ * This ensures that no errors are reported for this token.
+ */
+ this._desiredIndentCache.set(
+ token,
+ this._tokenInfo.getTokenIndent(token)
+ );
+ } else if (this._lockedFirstTokens.has(token)) {
+ const firstToken = this._lockedFirstTokens.get(token);
+
+ this._desiredIndentCache.set(
+ token,
+
+ // (indentation for the first element's line)
+ this.getDesiredIndent(this._tokenInfo.getFirstTokenOfLine(firstToken)) +
+
+ // (space between the start of the first element's line and the first element)
+ this._indentType.repeat(firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column)
+ );
+ } else {
+ const offsetInfo = this._getOffsetDescriptor(token);
+ const offset = (
+ offsetInfo.from &&
+ offsetInfo.from.loc.start.line === token.loc.start.line &&
+ !offsetInfo.force
+ ) ? 0 : offsetInfo.offset * this._indentSize;
+
+ this._desiredIndentCache.set(
+ token,
+ (offsetInfo.from ? this.getDesiredIndent(offsetInfo.from) : "") + this._indentType.repeat(offset)
+ );
+ }
+ }
+ return this._desiredIndentCache.get(token);
+ }
+
+ /**
+ * Ignores a token, preventing it from being reported.
+ * @param {Token} token The token
+ * @returns {void}
+ */
+ ignoreToken(token) {
+ if (this._tokenInfo.isFirstTokenOfLine(token)) {
+ this._ignoredTokens.add(token);
+ }
+ }
+
+ /**
+ * Gets the first token that the given token's indentation is dependent on
+ * @param {Token} token The token
+ * @returns {Token} The token that the given token depends on, or `null` if the given token is at the top level
+ */
+ getFirstDependency(token) {
+ return this._getOffsetDescriptor(token).from;
+ }
+}
+
+const ELEMENT_LIST_SCHEMA = {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["first", "off"]
+ }
+ ]
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent indentation",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["tab"]
+ },
+ {
+ type: "integer",
+ minimum: 0
+ }
+ ]
+ },
+ {
+ type: "object",
+ properties: {
+ SwitchCase: {
+ type: "integer",
+ minimum: 0
+ },
+ VariableDeclarator: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ var: {
+ type: "integer",
+ minimum: 0
+ },
+ let: {
+ type: "integer",
+ minimum: 0
+ },
+ const: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ outerIIFEBody: {
+ type: "integer",
+ minimum: 0
+ },
+ MemberExpression: {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["off"]
+ }
+ ]
+ },
+ FunctionDeclaration: {
+ type: "object",
+ properties: {
+ parameters: ELEMENT_LIST_SCHEMA,
+ body: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ },
+ FunctionExpression: {
+ type: "object",
+ properties: {
+ parameters: ELEMENT_LIST_SCHEMA,
+ body: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ },
+ CallExpression: {
+ type: "object",
+ properties: {
+ arguments: ELEMENT_LIST_SCHEMA
+ },
+ additionalProperties: false
+ },
+ ArrayExpression: ELEMENT_LIST_SCHEMA,
+ ObjectExpression: ELEMENT_LIST_SCHEMA,
+ ImportDeclaration: ELEMENT_LIST_SCHEMA,
+ flatTernaryExpressions: {
+ type: "boolean"
+ },
+ ignoredNodes: {
+ type: "array",
+ items: {
+ type: "string",
+ not: {
+ pattern: ":exit$"
+ }
+ }
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const DEFAULT_VARIABLE_INDENT = 1;
+ const DEFAULT_PARAMETER_INDENT = 1;
+ const DEFAULT_FUNCTION_BODY_INDENT = 1;
+
+ let indentType = "space";
+ let indentSize = 4;
+ const options = {
+ SwitchCase: 0,
+ VariableDeclarator: {
+ var: DEFAULT_VARIABLE_INDENT,
+ let: DEFAULT_VARIABLE_INDENT,
+ const: DEFAULT_VARIABLE_INDENT
+ },
+ outerIIFEBody: 1,
+ FunctionDeclaration: {
+ parameters: DEFAULT_PARAMETER_INDENT,
+ body: DEFAULT_FUNCTION_BODY_INDENT
+ },
+ FunctionExpression: {
+ parameters: DEFAULT_PARAMETER_INDENT,
+ body: DEFAULT_FUNCTION_BODY_INDENT
+ },
+ CallExpression: {
+ arguments: DEFAULT_PARAMETER_INDENT
+ },
+ MemberExpression: 1,
+ ArrayExpression: 1,
+ ObjectExpression: 1,
+ ImportDeclaration: 1,
+ flatTernaryExpressions: false,
+ ignoredNodes: []
+ };
+
+ if (context.options.length) {
+ if (context.options[0] === "tab") {
+ indentSize = 1;
+ indentType = "tab";
+ } else {
+ indentSize = context.options[0];
+ indentType = "space";
+ }
+
+ if (context.options[1]) {
+ lodash.merge(options, context.options[1]);
+
+ if (typeof options.VariableDeclarator === "number") {
+ options.VariableDeclarator = {
+ var: options.VariableDeclarator,
+ let: options.VariableDeclarator,
+ const: options.VariableDeclarator
+ };
+ }
+ }
+ }
+
+ const sourceCode = context.getSourceCode();
+ const tokenInfo = new TokenInfo(sourceCode);
+ const offsets = new OffsetStorage(tokenInfo, indentSize, indentType === "space" ? " " : "\t");
+ const parameterParens = new WeakSet();
+
+ /**
+ * Creates an error message for a line, given the expected/actual indentation.
+ * @param {int} expectedAmount The expected amount of indentation characters for this line
+ * @param {int} actualSpaces The actual number of indentation spaces that were found on this line
+ * @param {int} actualTabs The actual number of indentation tabs that were found on this line
+ * @returns {string} An error message for this line
+ */
+ function createErrorMessage(expectedAmount, actualSpaces, actualTabs) {
+ const expectedStatement = `${expectedAmount} ${indentType}${expectedAmount === 1 ? "" : "s"}`; // e.g. "2 tabs"
+ const foundSpacesWord = `space${actualSpaces === 1 ? "" : "s"}`; // e.g. "space"
+ const foundTabsWord = `tab${actualTabs === 1 ? "" : "s"}`; // e.g. "tabs"
+ let foundStatement;
+
+ if (actualSpaces > 0) {
+
+ /*
+ * Abbreviate the message if the expected indentation is also spaces.
+ * e.g. 'Expected 4 spaces but found 2' rather than 'Expected 4 spaces but found 2 spaces'
+ */
+ foundStatement = indentType === "space" ? actualSpaces : `${actualSpaces} ${foundSpacesWord}`;
+ } else if (actualTabs > 0) {
+ foundStatement = indentType === "tab" ? actualTabs : `${actualTabs} ${foundTabsWord}`;
+ } else {
+ foundStatement = "0";
+ }
+
+ return `Expected indentation of ${expectedStatement} but found ${foundStatement}.`;
+ }
+
+ /**
+ * Reports a given indent violation
+ * @param {Token} token Token violating the indent rule
+ * @param {string} neededIndent Expected indentation string
+ * @returns {void}
+ */
+ function report(token, neededIndent) {
+ const actualIndent = Array.from(tokenInfo.getTokenIndent(token));
+ const numSpaces = actualIndent.filter(char => char === " ").length;
+ const numTabs = actualIndent.filter(char => char === "\t").length;
+
+ context.report({
+ node: token,
+ message: createErrorMessage(neededIndent.length, numSpaces, numTabs),
+ loc: {
+ start: { line: token.loc.start.line, column: 0 },
+ end: { line: token.loc.start.line, column: token.loc.start.column }
+ },
+ fix(fixer) {
+ const range = [token.range[0] - token.loc.start.column, token.range[0]];
+ const newText = neededIndent;
+
+ return fixer.replaceTextRange(range, newText);
+ }
+ });
+ }
+
+ /**
+ * Checks if a token's indentation is correct
+ * @param {Token} token Token to examine
+ * @param {string} desiredIndent Desired indentation of the string
+ * @returns {boolean} `true` if the token's indentation is correct
+ */
+ function validateTokenIndent(token, desiredIndent) {
+ const indentation = tokenInfo.getTokenIndent(token);
+
+ return indentation === desiredIndent ||
+
+ // To avoid conflicts with no-mixed-spaces-and-tabs, don't report mixed spaces and tabs.
+ indentation.includes(" ") && indentation.includes("\t");
+ }
+
+ /**
+ * Check to see if the node is a file level IIFE
+ * @param {ASTNode} node The function node to check.
+ * @returns {boolean} True if the node is the outer IIFE
+ */
+ function isOuterIIFE(node) {
+
+ /*
+ * Verify that the node is an IIFE
+ */
+ if (!node.parent || node.parent.type !== "CallExpression" || node.parent.callee !== node) {
+ return false;
+ }
+
+ /*
+ * Navigate legal ancestors to determine whether this IIFE is outer.
+ * A "legal ancestor" is an expression or statement that causes the function to get executed immediately.
+ * For example, `!(function(){})()` is an outer IIFE even though it is preceded by a ! operator.
+ */
+ let statement = node.parent && node.parent.parent;
+
+ while (
+ statement.type === "UnaryExpression" && ["!", "~", "+", "-"].indexOf(statement.operator) > -1 ||
+ statement.type === "AssignmentExpression" ||
+ statement.type === "LogicalExpression" ||
+ statement.type === "SequenceExpression" ||
+ statement.type === "VariableDeclarator"
+ ) {
+ statement = statement.parent;
+ }
+
+ return (statement.type === "ExpressionStatement" || statement.type === "VariableDeclaration") && statement.parent.type === "Program";
+ }
+
+ /**
+ * Check indentation for lists of elements (arrays, objects, function params)
+ * @param {ASTNode[]} elements List of elements that should be offset
+ * @param {Token} startToken The start token of the list that element should be aligned against, e.g. '['
+ * @param {Token} endToken The end token of the list, e.g. ']'
+ * @param {number|string} offset The amount that the elements should be offset
+ * @returns {void}
+ */
+ function addElementListIndent(elements, startToken, endToken, offset) {
+
+ /**
+ * Gets the first token of a given element, including surrounding parentheses.
+ * @param {ASTNode} element A node in the `elements` list
+ * @returns {Token} The first token of this element
+ */
+ function getFirstToken(element) {
+ let token = sourceCode.getTokenBefore(element);
+
+ while (astUtils.isOpeningParenToken(token) && token !== startToken) {
+ token = sourceCode.getTokenBefore(token);
+ }
+ return sourceCode.getTokenAfter(token);
+ }
+
+ // Run through all the tokens in the list, and offset them by one indent level (mainly for comments, other things will end up overridden)
+ offsets.setDesiredOffsets(
+ [startToken.range[1], endToken.range[0]],
+ startToken,
+ typeof offset === "number" ? offset : 1
+ );
+ offsets.setDesiredOffset(endToken, startToken, 0);
+
+ // If the preference is "first" but there is no first element (e.g. sparse arrays w/ empty first slot), fall back to 1 level.
+ if (offset === "first" && elements.length && !elements[0]) {
+ return;
+ }
+ elements.forEach((element, index) => {
+ if (!element) {
+
+ // Skip holes in arrays
+ return;
+ }
+ if (offset === "off") {
+
+ // Ignore the first token of every element if the "off" option is used
+ offsets.ignoreToken(getFirstToken(element));
+ }
+
+ // Offset the following elements correctly relative to the first element
+ if (index === 0) {
+ return;
+ }
+ if (offset === "first" && tokenInfo.isFirstTokenOfLine(getFirstToken(element))) {
+ offsets.matchOffsetOf(getFirstToken(elements[0]), getFirstToken(element));
+ } else {
+ const previousElement = elements[index - 1];
+ const firstTokenOfPreviousElement = previousElement && getFirstToken(previousElement);
+
+ if (previousElement && sourceCode.getLastToken(previousElement).loc.start.line > startToken.loc.end.line) {
+ offsets.setDesiredOffsets(element.range, firstTokenOfPreviousElement, 0);
+ }
+ }
+ });
+ }
+
+ /**
+ * Check and decide whether to check for indentation for blockless nodes
+ * Scenarios are for or while statements without braces around them
+ * @param {ASTNode} node node to examine
+ * @returns {void}
+ */
+ function addBlocklessNodeIndent(node) {
+ if (node.type !== "BlockStatement") {
+ const lastParentToken = sourceCode.getTokenBefore(node, astUtils.isNotOpeningParenToken);
+
+ let firstBodyToken = sourceCode.getFirstToken(node);
+ let lastBodyToken = sourceCode.getLastToken(node);
+
+ while (
+ astUtils.isOpeningParenToken(sourceCode.getTokenBefore(firstBodyToken)) &&
+ astUtils.isClosingParenToken(sourceCode.getTokenAfter(lastBodyToken))
+ ) {
+ firstBodyToken = sourceCode.getTokenBefore(firstBodyToken);
+ lastBodyToken = sourceCode.getTokenAfter(lastBodyToken);
+ }
+
+ offsets.setDesiredOffsets([firstBodyToken.range[0], lastBodyToken.range[1]], lastParentToken, 1);
+
+ /*
+ * For blockless nodes with semicolon-first style, don't indent the semicolon.
+ * e.g.
+ * if (foo) bar()
+ * ; [1, 2, 3].map(foo)
+ */
+ const lastToken = sourceCode.getLastToken(node);
+
+ if (node.type !== "EmptyStatement" && astUtils.isSemicolonToken(lastToken)) {
+ offsets.setDesiredOffset(lastToken, lastParentToken, 0);
+ }
+ }
+ }
+
+ /**
+ * Checks the indentation for nodes that are like function calls (`CallExpression` and `NewExpression`)
+ * @param {ASTNode} node A CallExpression or NewExpression node
+ * @returns {void}
+ */
+ function addFunctionCallIndent(node) {
+ let openingParen;
+
+ if (node.arguments.length) {
+ openingParen = sourceCode.getFirstTokenBetween(node.callee, node.arguments[0], astUtils.isOpeningParenToken);
+ } else {
+ openingParen = sourceCode.getLastToken(node, 1);
+ }
+ const closingParen = sourceCode.getLastToken(node);
+
+ parameterParens.add(openingParen);
+ parameterParens.add(closingParen);
+ offsets.setDesiredOffset(openingParen, sourceCode.getTokenBefore(openingParen), 0);
+
+ addElementListIndent(node.arguments, openingParen, closingParen, options.CallExpression.arguments);
+ }
+
+ /**
+ * Checks the indentation of parenthesized values, given a list of tokens in a program
+ * @param {Token[]} tokens A list of tokens
+ * @returns {void}
+ */
+ function addParensIndent(tokens) {
+ const parenStack = [];
+ const parenPairs = [];
+
+ tokens.forEach(nextToken => {
+
+ // Accumulate a list of parenthesis pairs
+ if (astUtils.isOpeningParenToken(nextToken)) {
+ parenStack.push(nextToken);
+ } else if (astUtils.isClosingParenToken(nextToken)) {
+ parenPairs.unshift({ left: parenStack.pop(), right: nextToken });
+ }
+ });
+
+ parenPairs.forEach(pair => {
+ const leftParen = pair.left;
+ const rightParen = pair.right;
+
+ // We only want to handle parens around expressions, so exclude parentheses that are in function parameters and function call arguments.
+ if (!parameterParens.has(leftParen) && !parameterParens.has(rightParen)) {
+ const parenthesizedTokens = new Set(sourceCode.getTokensBetween(leftParen, rightParen));
+
+ parenthesizedTokens.forEach(token => {
+ if (!parenthesizedTokens.has(offsets.getFirstDependency(token))) {
+ offsets.setDesiredOffset(token, leftParen, 1);
+ }
+ });
+ }
+
+ offsets.setDesiredOffset(rightParen, leftParen, 0);
+ });
+ }
+
+ /**
+ * Ignore all tokens within an unknown node whose offset do not depend
+ * on another token's offset within the unknown node
+ * @param {ASTNode} node Unknown Node
+ * @returns {void}
+ */
+ function ignoreNode(node) {
+ const unknownNodeTokens = new Set(sourceCode.getTokens(node, { includeComments: true }));
+
+ unknownNodeTokens.forEach(token => {
+ if (!unknownNodeTokens.has(offsets.getFirstDependency(token))) {
+ const firstTokenOfLine = tokenInfo.getFirstTokenOfLine(token);
+
+ if (token === firstTokenOfLine) {
+ offsets.ignoreToken(token);
+ } else {
+ offsets.setDesiredOffset(token, firstTokenOfLine, 0);
+ }
+ }
+ });
+ }
+
+ /**
+ * Check whether the given token is on the first line of a statement.
+ * @param {Token} token The token to check.
+ * @param {ASTNode} leafNode The expression node that the token belongs directly.
+ * @returns {boolean} `true` if the token is on the first line of a statement.
+ */
+ function isOnFirstLineOfStatement(token, leafNode) {
+ let node = leafNode;
+
+ while (node.parent && !node.parent.type.endsWith("Statement") && !node.parent.type.endsWith("Declaration")) {
+ node = node.parent;
+ }
+ node = node.parent;
+
+ return !node || node.loc.start.line === token.loc.start.line;
+ }
+
+ const baseOffsetListeners = {
+ "ArrayExpression, ArrayPattern"(node) {
+ const openingBracket = sourceCode.getFirstToken(node);
+ const closingBracket = sourceCode.getTokenAfter(lodash.findLast(node.elements) || openingBracket, astUtils.isClosingBracketToken);
+
+ addElementListIndent(node.elements, openingBracket, closingBracket, options.ArrayExpression);
+ },
+
+ "ObjectExpression, ObjectPattern"(node) {
+ const openingCurly = sourceCode.getFirstToken(node);
+ const closingCurly = sourceCode.getTokenAfter(
+ node.properties.length ? node.properties[node.properties.length - 1] : openingCurly,
+ astUtils.isClosingBraceToken
+ );
+
+ addElementListIndent(node.properties, openingCurly, closingCurly, options.ObjectExpression);
+ },
+
+ ArrowFunctionExpression(node) {
+ const firstToken = sourceCode.getFirstToken(node);
+
+ if (astUtils.isOpeningParenToken(firstToken)) {
+ const openingParen = firstToken;
+ const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken);
+
+ parameterParens.add(openingParen);
+ parameterParens.add(closingParen);
+ addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
+ }
+ addBlocklessNodeIndent(node.body);
+
+ let arrowToken;
+
+ if (node.params.length) {
+ arrowToken = sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isArrowToken);
+ } else {
+ arrowToken = sourceCode.getFirstToken(node, astUtils.isArrowToken);
+ }
+ offsets.setDesiredOffset(arrowToken, sourceCode.getFirstToken(node), 0);
+ },
+
+ AssignmentExpression(node) {
+ const operator = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
+
+ offsets.setDesiredOffsets([operator.range[0], node.range[1]], sourceCode.getLastToken(node.left), 1);
+ offsets.ignoreToken(operator);
+ offsets.ignoreToken(sourceCode.getTokenAfter(operator));
+ },
+
+ "BinaryExpression, LogicalExpression"(node) {
+ const operator = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
+
+ /*
+ * For backwards compatibility, don't check BinaryExpression indents, e.g.
+ * var foo = bar &&
+ * baz;
+ */
+
+ const tokenAfterOperator = sourceCode.getTokenAfter(operator);
+
+ offsets.ignoreToken(operator);
+ offsets.ignoreToken(tokenAfterOperator);
+ offsets.setDesiredOffset(tokenAfterOperator, operator, 0);
+ offsets.setDesiredOffsets([tokenAfterOperator.range[1], node.range[1]], tokenAfterOperator, 1);
+ },
+
+ "BlockStatement, ClassBody"(node) {
+
+ let blockIndentLevel;
+
+ if (node.parent && isOuterIIFE(node.parent)) {
+ blockIndentLevel = options.outerIIFEBody;
+ } else if (node.parent && (node.parent.type === "FunctionExpression" || node.parent.type === "ArrowFunctionExpression")) {
+ blockIndentLevel = options.FunctionExpression.body;
+ } else if (node.parent && node.parent.type === "FunctionDeclaration") {
+ blockIndentLevel = options.FunctionDeclaration.body;
+ } else {
+ blockIndentLevel = 1;
+ }
+
+ /*
+ * For blocks that aren't lone statements, ensure that the opening curly brace
+ * is aligned with the parent.
+ */
+ if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
+ offsets.setDesiredOffset(sourceCode.getFirstToken(node), sourceCode.getFirstToken(node.parent), 0);
+ }
+ addElementListIndent(node.body, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), blockIndentLevel);
+ },
+
+ CallExpression: addFunctionCallIndent,
+
+
+ "ClassDeclaration[superClass], ClassExpression[superClass]"(node) {
+ const classToken = sourceCode.getFirstToken(node);
+ const extendsToken = sourceCode.getTokenBefore(node.superClass, astUtils.isNotOpeningParenToken);
+
+ offsets.setDesiredOffsets([extendsToken.range[0], node.body.range[0]], classToken, 1);
+ },
+
+ ConditionalExpression(node) {
+ const firstToken = sourceCode.getFirstToken(node);
+
+ // `flatTernaryExpressions` option is for the following style:
+ // var a =
+ // foo > 0 ? bar :
+ // foo < 0 ? baz :
+ // /*else*/ qiz ;
+ if (!options.flatTernaryExpressions ||
+ !astUtils.isTokenOnSameLine(node.test, node.consequent) ||
+ isOnFirstLineOfStatement(firstToken, node)
+ ) {
+ const questionMarkToken = sourceCode.getFirstTokenBetween(node.test, node.consequent, token => token.type === "Punctuator" && token.value === "?");
+ const colonToken = sourceCode.getFirstTokenBetween(node.consequent, node.alternate, token => token.type === "Punctuator" && token.value === ":");
+
+ const firstConsequentToken = sourceCode.getTokenAfter(questionMarkToken, { includeComments: true });
+ const lastConsequentToken = sourceCode.getTokenBefore(colonToken, { includeComments: true });
+ const firstAlternateToken = sourceCode.getTokenAfter(colonToken);
+
+ offsets.setDesiredOffset(questionMarkToken, firstToken, 1);
+ offsets.setDesiredOffset(colonToken, firstToken, 1);
+
+ offsets.setDesiredOffset(firstConsequentToken, firstToken, 1);
+
+ /*
+ * The alternate and the consequent should usually have the same indentation.
+ * If they share part of a line, align the alternate against the first token of the consequent.
+ * This allows the alternate to be indented correctly in cases like this:
+ * foo ? (
+ * bar
+ * ) : ( // this '(' is aligned with the '(' above, so it's considered to be aligned with `foo`
+ * baz // as a result, `baz` is offset by 1 rather than 2
+ * )
+ */
+ if (lastConsequentToken.loc.end.line === firstAlternateToken.loc.start.line) {
+ offsets.setDesiredOffset(firstAlternateToken, firstConsequentToken, 0);
+ } else {
+
+ /**
+ * If the alternate and consequent do not share part of a line, offset the alternate from the first
+ * token of the conditional expression. For example:
+ * foo ? bar
+ * : baz
+ *
+ * If `baz` were aligned with `bar` rather than being offset by 1 from `foo`, `baz` would end up
+ * having no expected indentation.
+ */
+ offsets.setDesiredOffset(firstAlternateToken, firstToken, 1);
+ }
+
+ offsets.setDesiredOffsets([questionMarkToken.range[1], colonToken.range[0]], firstConsequentToken, 0);
+ offsets.setDesiredOffsets([colonToken.range[1], node.range[1]], firstAlternateToken, 0);
+ }
+ },
+
+ "DoWhileStatement, WhileStatement, ForInStatement, ForOfStatement": node => addBlocklessNodeIndent(node.body),
+
+ ExportNamedDeclaration(node) {
+ if (node.declaration === null) {
+ const closingCurly = sourceCode.getLastToken(node, astUtils.isClosingBraceToken);
+
+ // Indent the specifiers in `export {foo, bar, baz}`
+ addElementListIndent(node.specifiers, sourceCode.getFirstToken(node, { skip: 1 }), closingCurly, 1);
+
+ if (node.source) {
+
+ // Indent everything after and including the `from` token in `export {foo, bar, baz} from 'qux'`
+ offsets.setDesiredOffsets([closingCurly.range[1], node.range[1]], sourceCode.getFirstToken(node), 1);
+ }
+ }
+ },
+
+ ForStatement(node) {
+ const forOpeningParen = sourceCode.getFirstToken(node, 1);
+
+ if (node.init) {
+ offsets.setDesiredOffsets(node.init.range, forOpeningParen, 1);
+ }
+ if (node.test) {
+ offsets.setDesiredOffsets(node.test.range, forOpeningParen, 1);
+ }
+ if (node.update) {
+ offsets.setDesiredOffsets(node.update.range, forOpeningParen, 1);
+ }
+ addBlocklessNodeIndent(node.body);
+ },
+
+ "FunctionDeclaration, FunctionExpression"(node) {
+ const closingParen = sourceCode.getTokenBefore(node.body);
+ const openingParen = sourceCode.getTokenBefore(node.params.length ? node.params[0] : closingParen);
+
+ parameterParens.add(openingParen);
+ parameterParens.add(closingParen);
+ addElementListIndent(node.params, openingParen, closingParen, options[node.type].parameters);
+ },
+
+ IfStatement(node) {
+ addBlocklessNodeIndent(node.consequent);
+ if (node.alternate && node.alternate.type !== "IfStatement") {
+ addBlocklessNodeIndent(node.alternate);
+ }
+ },
+
+ ImportDeclaration(node) {
+ if (node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) {
+ const openingCurly = sourceCode.getFirstToken(node, astUtils.isOpeningBraceToken);
+ const closingCurly = sourceCode.getLastToken(node, astUtils.isClosingBraceToken);
+
+ addElementListIndent(node.specifiers.filter(specifier => specifier.type === "ImportSpecifier"), openingCurly, closingCurly, options.ImportDeclaration);
+ }
+
+ const fromToken = sourceCode.getLastToken(node, token => token.type === "Identifier" && token.value === "from");
+
+ if (fromToken) {
+ offsets.setDesiredOffsets([fromToken.range[0], node.range[1]], sourceCode.getFirstToken(node), 1);
+ }
+ },
+
+ "MemberExpression, JSXMemberExpression, MetaProperty"(node) {
+ const object = node.type === "MetaProperty" ? node.meta : node.object;
+ const firstNonObjectToken = sourceCode.getFirstTokenBetween(object, node.property, astUtils.isNotClosingParenToken);
+ const secondNonObjectToken = sourceCode.getTokenAfter(firstNonObjectToken);
+
+ const objectParenCount = sourceCode.getTokensBetween(object, node.property, { filter: astUtils.isClosingParenToken }).length;
+ const firstObjectToken = objectParenCount
+ ? sourceCode.getTokenBefore(object, { skip: objectParenCount - 1 })
+ : sourceCode.getFirstToken(object);
+ const lastObjectToken = sourceCode.getTokenBefore(firstNonObjectToken);
+ const firstPropertyToken = node.computed ? firstNonObjectToken : secondNonObjectToken;
+
+ if (node.computed) {
+
+ // For computed MemberExpressions, match the closing bracket with the opening bracket.
+ offsets.setDesiredOffset(sourceCode.getLastToken(node), firstNonObjectToken, 0);
+ offsets.setDesiredOffsets(node.property.range, firstNonObjectToken, 1);
+ }
+
+ /*
+ * If the object ends on the same line that the property starts, match against the last token
+ * of the object, to ensure that the MemberExpression is not indented.
+ *
+ * Otherwise, match against the first token of the object, e.g.
+ * foo
+ * .bar
+ * .baz // <-- offset by 1 from `foo`
+ */
+ const offsetBase = lastObjectToken.loc.end.line === firstPropertyToken.loc.start.line
+ ? lastObjectToken
+ : firstObjectToken;
+
+ if (typeof options.MemberExpression === "number") {
+
+ // Match the dot (for non-computed properties) or the opening bracket (for computed properties) against the object.
+ offsets.setDesiredOffset(firstNonObjectToken, offsetBase, options.MemberExpression);
+
+ /*
+ * For computed MemberExpressions, match the first token of the property against the opening bracket.
+ * Otherwise, match the first token of the property against the object.
+ */
+ offsets.setDesiredOffset(secondNonObjectToken, node.computed ? firstNonObjectToken : offsetBase, options.MemberExpression);
+ } else {
+
+ // If the MemberExpression option is off, ignore the dot and the first token of the property.
+ offsets.ignoreToken(firstNonObjectToken);
+ offsets.ignoreToken(secondNonObjectToken);
+
+ // To ignore the property indentation, ensure that the property tokens depend on the ignored tokens.
+ offsets.setDesiredOffset(firstNonObjectToken, offsetBase, 0);
+ offsets.setDesiredOffset(secondNonObjectToken, firstNonObjectToken, 0);
+ }
+ },
+
+ NewExpression(node) {
+
+ // Only indent the arguments if the NewExpression has parens (e.g. `new Foo(bar)` or `new Foo()`, but not `new Foo`
+ if (node.arguments.length > 0 ||
+ astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
+ astUtils.isOpeningParenToken(sourceCode.getLastToken(node, 1))) {
+ addFunctionCallIndent(node);
+ }
+ },
+
+ Property(node) {
+ if (!node.shorthand && !node.method && node.kind === "init") {
+ const colon = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isColonToken);
+
+ offsets.ignoreToken(sourceCode.getTokenAfter(colon));
+ }
+ },
+
+ SwitchStatement(node) {
+ const openingCurly = sourceCode.getTokenAfter(node.discriminant, astUtils.isOpeningBraceToken);
+ const closingCurly = sourceCode.getLastToken(node);
+ const caseKeywords = node.cases.map(switchCase => sourceCode.getFirstToken(switchCase));
+
+ offsets.setDesiredOffsets([openingCurly.range[1], closingCurly.range[0]], openingCurly, options.SwitchCase);
+
+ node.cases.forEach((switchCase, index) => {
+ const caseKeyword = caseKeywords[index];
+
+ if (!(switchCase.consequent.length === 1 && switchCase.consequent[0].type === "BlockStatement")) {
+ const tokenAfterCurrentCase = index === node.cases.length - 1 ? closingCurly : caseKeywords[index + 1];
+
+ offsets.setDesiredOffsets([caseKeyword.range[1], tokenAfterCurrentCase.range[0]], caseKeyword, 1);
+ }
+ });
+
+ if (node.cases.length) {
+ sourceCode.getTokensBetween(
+ node.cases[node.cases.length - 1],
+ closingCurly,
+ { includeComments: true, filter: astUtils.isCommentToken }
+ ).forEach(token => offsets.ignoreToken(token));
+ }
+ },
+
+ TemplateLiteral(node) {
+ node.expressions.forEach((expression, index) => {
+ const previousQuasi = node.quasis[index];
+ const nextQuasi = node.quasis[index + 1];
+ const tokenToAlignFrom = previousQuasi.loc.start.line === previousQuasi.loc.end.line ? sourceCode.getFirstToken(previousQuasi) : null;
+
+ offsets.setDesiredOffsets([previousQuasi.range[1], nextQuasi.range[0]], tokenToAlignFrom, 1);
+ offsets.setDesiredOffset(sourceCode.getFirstToken(nextQuasi), tokenToAlignFrom, 0);
+ });
+ },
+
+ VariableDeclaration(node) {
+ const variableIndent = options.VariableDeclarator.hasOwnProperty(node.kind) ? options.VariableDeclarator[node.kind] : DEFAULT_VARIABLE_INDENT;
+
+ if (node.declarations[node.declarations.length - 1].loc.start.line > node.loc.start.line) {
+
+ /*
+ * VariableDeclarator indentation is a bit different from other forms of indentation, in that the
+ * indentation of an opening bracket sometimes won't match that of a closing bracket. For example,
+ * the following indentations are correct:
+ *
+ * var foo = {
+ * ok: true
+ * };
+ *
+ * var foo = {
+ * ok: true,
+ * },
+ * bar = 1;
+ *
+ * Account for when exiting the AST (after indentations have already been set for the nodes in
+ * the declaration) by manually increasing the indentation level of the tokens in this declarator
+ * on the same line as the start of the declaration, provided that there are declarators that
+ * follow this one.
+ */
+ const firstToken = sourceCode.getFirstToken(node);
+
+ offsets.setDesiredOffsets(node.range, firstToken, variableIndent, true);
+ } else {
+ offsets.setDesiredOffsets(node.range, sourceCode.getFirstToken(node), variableIndent);
+ }
+ const lastToken = sourceCode.getLastToken(node);
+
+ if (astUtils.isSemicolonToken(lastToken)) {
+ offsets.ignoreToken(lastToken);
+ }
+ },
+
+ VariableDeclarator(node) {
+ if (node.init) {
+ const equalOperator = sourceCode.getTokenBefore(node.init, astUtils.isNotOpeningParenToken);
+ const tokenAfterOperator = sourceCode.getTokenAfter(equalOperator);
+
+ offsets.ignoreToken(equalOperator);
+ offsets.ignoreToken(tokenAfterOperator);
+ offsets.setDesiredOffsets([tokenAfterOperator.range[0], node.range[1]], equalOperator, 1);
+ offsets.setDesiredOffset(equalOperator, sourceCode.getLastToken(node.id), 0);
+ }
+ },
+
+ "JSXAttribute[value]"(node) {
+ const equalsToken = sourceCode.getFirstTokenBetween(node.name, node.value, token => token.type === "Punctuator" && token.value === "=");
+
+ offsets.setDesiredOffsets([equalsToken.range[0], node.value.range[1]], sourceCode.getFirstToken(node.name), 1);
+ },
+
+ JSXElement(node) {
+ if (node.closingElement) {
+ addElementListIndent(node.children, sourceCode.getFirstToken(node.openingElement), sourceCode.getFirstToken(node.closingElement), 1);
+ }
+ },
+
+ JSXOpeningElement(node) {
+ const firstToken = sourceCode.getFirstToken(node);
+ let closingToken;
+
+ if (node.selfClosing) {
+ closingToken = sourceCode.getLastToken(node, { skip: 1 });
+ offsets.setDesiredOffset(sourceCode.getLastToken(node), closingToken, 0);
+ } else {
+ closingToken = sourceCode.getLastToken(node);
+ }
+ offsets.setDesiredOffsets(node.name.range, sourceCode.getFirstToken(node));
+ addElementListIndent(node.attributes, firstToken, closingToken, 1);
+ },
+
+ JSXClosingElement(node) {
+ const firstToken = sourceCode.getFirstToken(node);
+
+ offsets.setDesiredOffsets(node.name.range, firstToken, 1);
+ offsets.setDesiredOffset(sourceCode.getLastToken(node), firstToken, 0);
+ },
+
+ JSXExpressionContainer(node) {
+ const openingCurly = sourceCode.getFirstToken(node);
+ const closingCurly = sourceCode.getLastToken(node);
+
+ offsets.setDesiredOffsets(
+ [openingCurly.range[1], closingCurly.range[0]],
+ openingCurly,
+ 1
+ );
+ offsets.setDesiredOffset(closingCurly, openingCurly, 0);
+ }
+ };
+
+ const listenerCallQueue = [];
+
+ /*
+ * To ignore the indentation of a node:
+ * 1. Don't call the node's listener when entering it (if it has a listener)
+ * 2. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
+ */
+ const offsetListeners = lodash.mapValues(
+ baseOffsetListeners,
+
+ /*
+ * Offset listener calls are deferred until traversal is finished, and are called as
+ * part of the final `Program:exit` listener. This is necessary because a node might
+ * be matched by multiple selectors.
+ *
+ * Example: Suppose there is an offset listener for `Identifier`, and the user has
+ * specified in configuration that `MemberExpression > Identifier` should be ignored.
+ * Due to selector specificity rules, the `Identifier` listener will get called first. However,
+ * if a given Identifier node is supposed to be ignored, then the `Identifier` offset listener
+ * should not have been called at all. Without doing extra selector matching, we don't know
+ * whether the Identifier matches the `MemberExpression > Identifier` selector until the
+ * `MemberExpression > Identifier` listener is called.
+ *
+ * To avoid this, the `Identifier` listener isn't called until traversal finishes and all
+ * ignored nodes are known.
+ */
+ listener =>
+ node =>
+ listenerCallQueue.push({ listener, node })
+ );
+
+ // For each ignored node selector, set up a listener to collect it into the `ignoredNodes` set.
+ const ignoredNodes = new Set();
+ const addToIgnoredNodes = ignoredNodes.add.bind(ignoredNodes);
+
+ const ignoredNodeListeners = options.ignoredNodes.reduce(
+ (listeners, ignoredSelector) => Object.assign(listeners, { [ignoredSelector]: addToIgnoredNodes }),
+ {}
+ );
+
+ /*
+ * Join the listeners, and add a listener to verify that all tokens actually have the correct indentation
+ * at the end.
+ *
+ * Using Object.assign will cause some offset listeners to be overwritten if the same selector also appears
+ * in `ignoredNodeListeners`. This isn't a problem because all of the matching nodes will be ignored,
+ * so those listeners wouldn't be called anyway.
+ */
+ return Object.assign(
+ offsetListeners,
+ ignoredNodeListeners,
+ {
+ "*:exit"(node) {
+
+ // If a node's type is nonstandard, we can't tell how its children should be offset, so ignore it.
+ if (!KNOWN_NODES.has(node.type)) {
+ ignoredNodes.add(node);
+ }
+ },
+ "Program:exit"() {
+
+ // Invoke the queued offset listeners for the nodes that aren't ignored.
+ listenerCallQueue
+ .filter(nodeInfo => !ignoredNodes.has(nodeInfo.node))
+ .forEach(nodeInfo => nodeInfo.listener(nodeInfo.node));
+
+ // Update the offsets for ignored nodes to prevent their child tokens from being reported.
+ ignoredNodes.forEach(ignoreNode);
+
+ addParensIndent(sourceCode.ast.tokens);
+
+ /*
+ * Create a Map from (tokenOrComment) => (precedingToken).
+ * This is necessary because sourceCode.getTokenBefore does not handle a comment as an argument correctly.
+ */
+ const precedingTokens = sourceCode.ast.comments.reduce((commentMap, comment) => {
+ const tokenOrCommentBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
+
+ return commentMap.set(comment, commentMap.has(tokenOrCommentBefore) ? commentMap.get(tokenOrCommentBefore) : tokenOrCommentBefore);
+ }, new WeakMap());
+
+ sourceCode.lines.forEach((line, lineIndex) => {
+ const lineNumber = lineIndex + 1;
+
+ if (!tokenInfo.firstTokensByLineNumber.has(lineNumber)) {
+
+ // Don't check indentation on blank lines
+ return;
+ }
+
+ const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(lineNumber);
+
+ if (firstTokenOfLine.loc.start.line !== lineNumber) {
+
+ // Don't check the indentation of multi-line tokens (e.g. template literals or block comments) twice.
+ return;
+ }
+
+ // If the token matches the expected expected indentation, don't report it.
+ if (validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine))) {
+ return;
+ }
+
+ if (astUtils.isCommentToken(firstTokenOfLine)) {
+ const tokenBefore = precedingTokens.get(firstTokenOfLine);
+ const tokenAfter = tokenBefore ? sourceCode.getTokenAfter(tokenBefore) : sourceCode.ast.tokens[0];
+
+ // If a comment matches the expected indentation of the token immediately before or after, don't report it.
+ if (
+ tokenBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
+ tokenAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
+ ) {
+ return;
+ }
+ }
+
+ // Otherwise, report the token/comment.
+ report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
+ });
+ }
+ }
+ );
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/init-declarations.js b/tools/node_modules/eslint/lib/rules/init-declarations.js
new file mode 100644
index 0000000000..1f53f3dfc4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/init-declarations.js
@@ -0,0 +1,137 @@
+/**
+ * @fileoverview A rule to control the style of variable initializations.
+ * @author Colin Ihrig
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is a for loop.
+ * @param {ASTNode} block - A node to check.
+ * @returns {boolean} `true` when the node is a for loop.
+ */
+function isForLoop(block) {
+ return block.type === "ForInStatement" ||
+ block.type === "ForOfStatement" ||
+ block.type === "ForStatement";
+}
+
+/**
+ * Checks whether or not a given declarator node has its initializer.
+ * @param {ASTNode} node - A declarator node to check.
+ * @returns {boolean} `true` when the node has its initializer.
+ */
+function isInitialized(node) {
+ const declaration = node.parent;
+ const block = declaration.parent;
+
+ if (isForLoop(block)) {
+ if (block.type === "ForStatement") {
+ return block.init === declaration;
+ }
+ return block.left === declaration;
+ }
+ return Boolean(node.init);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow initialization in variable declarations",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["never"]
+ },
+ {
+ type: "object",
+ properties: {
+ ignoreForLoopInit: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ }
+ },
+
+ create(context) {
+
+ const MODE_ALWAYS = "always",
+ MODE_NEVER = "never";
+
+ const mode = context.options[0] || MODE_ALWAYS;
+ const params = context.options[1] || {};
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ "VariableDeclaration:exit"(node) {
+
+ const kind = node.kind,
+ declarations = node.declarations;
+
+ for (let i = 0; i < declarations.length; ++i) {
+ const declaration = declarations[i],
+ id = declaration.id,
+ initialized = isInitialized(declaration),
+ isIgnoredForLoop = params.ignoreForLoopInit && isForLoop(node.parent);
+
+ if (id.type !== "Identifier") {
+ continue;
+ }
+
+ if (mode === MODE_ALWAYS && !initialized) {
+ context.report({
+ node: declaration,
+ message: "Variable '{{idName}}' should be initialized on declaration.",
+ data: {
+ idName: id.name
+ }
+ });
+ } else if (mode === MODE_NEVER && kind !== "const" && initialized && !isIgnoredForLoop) {
+ context.report({
+ node: declaration,
+ message: "Variable '{{idName}}' should not be initialized on declaration.",
+ data: {
+ idName: id.name
+ }
+ });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/jsx-quotes.js b/tools/node_modules/eslint/lib/rules/jsx-quotes.js
new file mode 100644
index 0000000000..5653922d94
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/jsx-quotes.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview A rule to ensure consistent quotes used in jsx syntax.
+ * @author Mathias Schreck <https://github.com/lo1tuma>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const QUOTE_SETTINGS = {
+ "prefer-double": {
+ quote: "\"",
+ description: "singlequote",
+ convert(str) {
+ return str.replace(/'/g, "\"");
+ }
+ },
+ "prefer-single": {
+ quote: "'",
+ description: "doublequote",
+ convert(str) {
+ return str.replace(/"/g, "'");
+ }
+ }
+};
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the consistent use of either double or single quotes in JSX attributes",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["prefer-single", "prefer-double"]
+ }
+ ]
+ },
+
+ create(context) {
+ const quoteOption = context.options[0] || "prefer-double",
+ setting = QUOTE_SETTINGS[quoteOption];
+
+ /**
+ * Checks if the given string literal node uses the expected quotes
+ * @param {ASTNode} node - A string literal node.
+ * @returns {boolean} Whether or not the string literal used the expected quotes.
+ * @public
+ */
+ function usesExpectedQuotes(node) {
+ return node.value.indexOf(setting.quote) !== -1 || astUtils.isSurroundedBy(node.raw, setting.quote);
+ }
+
+ return {
+ JSXAttribute(node) {
+ const attributeValue = node.value;
+
+ if (attributeValue && astUtils.isStringLiteral(attributeValue) && !usesExpectedQuotes(attributeValue)) {
+ context.report({
+ node: attributeValue,
+ message: "Unexpected usage of {{description}}.",
+ data: {
+ description: setting.description
+ },
+ fix(fixer) {
+ return fixer.replaceText(attributeValue, setting.convert(attributeValue.raw));
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/key-spacing.js b/tools/node_modules/eslint/lib/rules/key-spacing.js
new file mode 100644
index 0000000000..1c62ad4289
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/key-spacing.js
@@ -0,0 +1,641 @@
+/**
+ * @fileoverview Rule to specify spacing of object literal keys and values
+ * @author Brandon Mills
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether a string contains a line terminator as defined in
+ * http://www.ecma-international.org/ecma-262/5.1/#sec-7.3
+ * @param {string} str String to test.
+ * @returns {boolean} True if str contains a line terminator.
+ */
+function containsLineTerminator(str) {
+ return astUtils.LINEBREAK_MATCHER.test(str);
+}
+
+/**
+ * Gets the last element of an array.
+ * @param {Array} arr An array.
+ * @returns {any} Last element of arr.
+ */
+function last(arr) {
+ return arr[arr.length - 1];
+}
+
+/**
+ * Checks whether a node is contained on a single line.
+ * @param {ASTNode} node AST Node being evaluated.
+ * @returns {boolean} True if the node is a single line.
+ */
+function isSingleLine(node) {
+ return (node.loc.end.line === node.loc.start.line);
+}
+
+/**
+ * Initializes a single option property from the configuration with defaults for undefined values
+ * @param {Object} toOptions Object to be initialized
+ * @param {Object} fromOptions Object to be initialized from
+ * @returns {Object} The object with correctly initialized options and values
+ */
+function initOptionProperty(toOptions, fromOptions) {
+ toOptions.mode = fromOptions.mode || "strict";
+
+ // Set value of beforeColon
+ if (typeof fromOptions.beforeColon !== "undefined") {
+ toOptions.beforeColon = +fromOptions.beforeColon;
+ } else {
+ toOptions.beforeColon = 0;
+ }
+
+ // Set value of afterColon
+ if (typeof fromOptions.afterColon !== "undefined") {
+ toOptions.afterColon = +fromOptions.afterColon;
+ } else {
+ toOptions.afterColon = 1;
+ }
+
+ // Set align if exists
+ if (typeof fromOptions.align !== "undefined") {
+ if (typeof fromOptions.align === "object") {
+ toOptions.align = fromOptions.align;
+ } else { // "string"
+ toOptions.align = {
+ on: fromOptions.align,
+ mode: toOptions.mode,
+ beforeColon: toOptions.beforeColon,
+ afterColon: toOptions.afterColon
+ };
+ }
+ }
+
+ return toOptions;
+}
+
+/**
+ * Initializes all the option values (singleLine, multiLine and align) from the configuration with defaults for undefined values
+ * @param {Object} toOptions Object to be initialized
+ * @param {Object} fromOptions Object to be initialized from
+ * @returns {Object} The object with correctly initialized options and values
+ */
+function initOptions(toOptions, fromOptions) {
+ if (typeof fromOptions.align === "object") {
+
+ // Initialize the alignment configuration
+ toOptions.align = initOptionProperty({}, fromOptions.align);
+ toOptions.align.on = fromOptions.align.on || "colon";
+ toOptions.align.mode = fromOptions.align.mode || "strict";
+
+ toOptions.multiLine = initOptionProperty({}, (fromOptions.multiLine || fromOptions));
+ toOptions.singleLine = initOptionProperty({}, (fromOptions.singleLine || fromOptions));
+
+ } else { // string or undefined
+ toOptions.multiLine = initOptionProperty({}, (fromOptions.multiLine || fromOptions));
+ toOptions.singleLine = initOptionProperty({}, (fromOptions.singleLine || fromOptions));
+
+ // If alignment options are defined in multiLine, pull them out into the general align configuration
+ if (toOptions.multiLine.align) {
+ toOptions.align = {
+ on: toOptions.multiLine.align.on,
+ mode: toOptions.multiLine.align.mode || toOptions.multiLine.mode,
+ beforeColon: toOptions.multiLine.align.beforeColon,
+ afterColon: toOptions.multiLine.align.afterColon
+ };
+ }
+ }
+
+ return toOptions;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const messages = {
+ key: "{{error}} space after {{computed}}key '{{key}}'.",
+ value: "{{error}} space before value for {{computed}}key '{{key}}'."
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing between keys and values in object literal properties",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [{
+ anyOf: [
+ {
+ type: "object",
+ properties: {
+ align: {
+ anyOf: [
+ {
+ enum: ["colon", "value"]
+ },
+ {
+ type: "object",
+ properties: {
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ on: {
+ enum: ["colon", "value"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ },
+ {
+ type: "object",
+ properties: {
+ singleLine: {
+ type: "object",
+ properties: {
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ },
+ multiLine: {
+ type: "object",
+ properties: {
+ align: {
+ anyOf: [
+ {
+ enum: ["colon", "value"]
+ },
+ {
+ type: "object",
+ properties: {
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ on: {
+ enum: ["colon", "value"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ },
+ {
+ type: "object",
+ properties: {
+ singleLine: {
+ type: "object",
+ properties: {
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ },
+ multiLine: {
+ type: "object",
+ properties: {
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ },
+ align: {
+ type: "object",
+ properties: {
+ mode: {
+ enum: ["strict", "minimum"]
+ },
+ on: {
+ enum: ["colon", "value"]
+ },
+ beforeColon: {
+ type: "boolean"
+ },
+ afterColon: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }]
+ },
+
+ create(context) {
+
+ /**
+ * OPTIONS
+ * "key-spacing": [2, {
+ * beforeColon: false,
+ * afterColon: true,
+ * align: "colon" // Optional, or "value"
+ * }
+ */
+ const options = context.options[0] || {},
+ ruleOptions = initOptions({}, options),
+ multiLineOptions = ruleOptions.multiLine,
+ singleLineOptions = ruleOptions.singleLine,
+ alignmentOptions = ruleOptions.align || null;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Checks whether a property is a member of the property group it follows.
+ * @param {ASTNode} lastMember The last Property known to be in the group.
+ * @param {ASTNode} candidate The next Property that might be in the group.
+ * @returns {boolean} True if the candidate property is part of the group.
+ */
+ function continuesPropertyGroup(lastMember, candidate) {
+ const groupEndLine = lastMember.loc.start.line,
+ candidateStartLine = candidate.loc.start.line;
+
+ if (candidateStartLine - groupEndLine <= 1) {
+ return true;
+ }
+
+ /*
+ * Check that the first comment is adjacent to the end of the group, the
+ * last comment is adjacent to the candidate property, and that successive
+ * comments are adjacent to each other.
+ */
+ const leadingComments = sourceCode.getCommentsBefore(candidate);
+
+ if (
+ leadingComments.length &&
+ leadingComments[0].loc.start.line - groupEndLine <= 1 &&
+ candidateStartLine - last(leadingComments).loc.end.line <= 1
+ ) {
+ for (let i = 1; i < leadingComments.length; i++) {
+ if (leadingComments[i].loc.start.line - leadingComments[i - 1].loc.end.line > 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if the given property is key-value property.
+ * @param {ASTNode} property Property node to check.
+ * @returns {boolean} Whether the property is a key-value property.
+ */
+ function isKeyValueProperty(property) {
+ return !(
+ (property.method ||
+ property.shorthand ||
+ property.kind !== "init" || property.type !== "Property") // Could be "ExperimentalSpreadProperty" or "SpreadProperty"
+ );
+ }
+
+ /**
+ * Starting from the given a node (a property.key node here) looks forward
+ * until it finds the last token before a colon punctuator and returns it.
+ * @param {ASTNode} node The node to start looking from.
+ * @returns {ASTNode} The last token before a colon punctuator.
+ */
+ function getLastTokenBeforeColon(node) {
+ const colonToken = sourceCode.getTokenAfter(node, astUtils.isColonToken);
+
+ return sourceCode.getTokenBefore(colonToken);
+ }
+
+ /**
+ * Starting from the given a node (a property.key node here) looks forward
+ * until it finds the colon punctuator and returns it.
+ * @param {ASTNode} node The node to start looking from.
+ * @returns {ASTNode} The colon punctuator.
+ */
+ function getNextColon(node) {
+ return sourceCode.getTokenAfter(node, astUtils.isColonToken);
+ }
+
+ /**
+ * Gets an object literal property's key as the identifier name or string value.
+ * @param {ASTNode} property Property node whose key to retrieve.
+ * @returns {string} The property's key.
+ */
+ function getKey(property) {
+ const key = property.key;
+
+ if (property.computed) {
+ return sourceCode.getText().slice(key.range[0], key.range[1]);
+ }
+
+ return property.key.name || property.key.value;
+ }
+
+ /**
+ * Reports an appropriately-formatted error if spacing is incorrect on one
+ * side of the colon.
+ * @param {ASTNode} property Key-value pair in an object literal.
+ * @param {string} side Side being verified - either "key" or "value".
+ * @param {string} whitespace Actual whitespace string.
+ * @param {int} expected Expected whitespace length.
+ * @param {string} mode Value of the mode as "strict" or "minimum"
+ * @returns {void}
+ */
+ function report(property, side, whitespace, expected, mode) {
+ const diff = whitespace.length - expected,
+ nextColon = getNextColon(property.key),
+ tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
+ tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
+ isKeySide = side === "key",
+ locStart = isKeySide ? tokenBeforeColon.loc.start : tokenAfterColon.loc.start,
+ isExtra = diff > 0,
+ diffAbs = Math.abs(diff),
+ spaces = Array(diffAbs + 1).join(" ");
+
+ if ((
+ diff && mode === "strict" ||
+ diff < 0 && mode === "minimum" ||
+ diff > 0 && !expected && mode === "minimum") &&
+ !(expected && containsLineTerminator(whitespace))
+ ) {
+ let fix;
+
+ if (isExtra) {
+ let range;
+
+ // Remove whitespace
+ if (isKeySide) {
+ range = [tokenBeforeColon.range[1], tokenBeforeColon.range[1] + diffAbs];
+ } else {
+ range = [tokenAfterColon.range[0] - diffAbs, tokenAfterColon.range[0]];
+ }
+ fix = function(fixer) {
+ return fixer.removeRange(range);
+ };
+ } else {
+
+ // Add whitespace
+ if (isKeySide) {
+ fix = function(fixer) {
+ return fixer.insertTextAfter(tokenBeforeColon, spaces);
+ };
+ } else {
+ fix = function(fixer) {
+ return fixer.insertTextBefore(tokenAfterColon, spaces);
+ };
+ }
+ }
+
+ context.report({
+ node: property[side],
+ loc: locStart,
+ message: messages[side],
+ data: {
+ error: isExtra ? "Extra" : "Missing",
+ computed: property.computed ? "computed " : "",
+ key: getKey(property)
+ },
+ fix
+ });
+ }
+ }
+
+ /**
+ * Gets the number of characters in a key, including quotes around string
+ * keys and braces around computed property keys.
+ * @param {ASTNode} property Property of on object literal.
+ * @returns {int} Width of the key.
+ */
+ function getKeyWidth(property) {
+ const startToken = sourceCode.getFirstToken(property);
+ const endToken = getLastTokenBeforeColon(property.key);
+
+ return endToken.range[1] - startToken.range[0];
+ }
+
+ /**
+ * Gets the whitespace around the colon in an object literal property.
+ * @param {ASTNode} property Property node from an object literal.
+ * @returns {Object} Whitespace before and after the property's colon.
+ */
+ function getPropertyWhitespace(property) {
+ const whitespace = /(\s*):(\s*)/.exec(sourceCode.getText().slice(
+ property.key.range[1], property.value.range[0]
+ ));
+
+ if (whitespace) {
+ return {
+ beforeColon: whitespace[1],
+ afterColon: whitespace[2]
+ };
+ }
+ return null;
+ }
+
+ /**
+ * Creates groups of properties.
+ * @param {ASTNode} node ObjectExpression node being evaluated.
+ * @returns {Array.<ASTNode[]>} Groups of property AST node lists.
+ */
+ function createGroups(node) {
+ if (node.properties.length === 1) {
+ return [node.properties];
+ }
+
+ return node.properties.reduce((groups, property) => {
+ const currentGroup = last(groups),
+ prev = last(currentGroup);
+
+ if (!prev || continuesPropertyGroup(prev, property)) {
+ currentGroup.push(property);
+ } else {
+ groups.push([property]);
+ }
+
+ return groups;
+ }, [
+ []
+ ]);
+ }
+
+ /**
+ * Verifies correct vertical alignment of a group of properties.
+ * @param {ASTNode[]} properties List of Property AST nodes.
+ * @returns {void}
+ */
+ function verifyGroupAlignment(properties) {
+ const length = properties.length,
+ widths = properties.map(getKeyWidth), // Width of keys, including quotes
+ align = alignmentOptions.on; // "value" or "colon"
+ let targetWidth = Math.max.apply(null, widths),
+ beforeColon, afterColon, mode;
+
+ if (alignmentOptions && length > 1) { // When aligning values within a group, use the alignment configuration.
+ beforeColon = alignmentOptions.beforeColon;
+ afterColon = alignmentOptions.afterColon;
+ mode = alignmentOptions.mode;
+ } else {
+ beforeColon = multiLineOptions.beforeColon;
+ afterColon = multiLineOptions.afterColon;
+ mode = alignmentOptions.mode;
+ }
+
+ // Conditionally include one space before or after colon
+ targetWidth += (align === "colon" ? beforeColon : afterColon);
+
+ for (let i = 0; i < length; i++) {
+ const property = properties[i];
+ const whitespace = getPropertyWhitespace(property);
+
+ if (whitespace) { // Object literal getters/setters lack a colon
+ const width = widths[i];
+
+ if (align === "value") {
+ report(property, "key", whitespace.beforeColon, beforeColon, mode);
+ report(property, "value", whitespace.afterColon, targetWidth - width, mode);
+ } else { // align = "colon"
+ report(property, "key", whitespace.beforeColon, targetWidth - width, mode);
+ report(property, "value", whitespace.afterColon, afterColon, mode);
+ }
+ }
+ }
+ }
+
+ /**
+ * Verifies vertical alignment, taking into account groups of properties.
+ * @param {ASTNode} node ObjectExpression node being evaluated.
+ * @returns {void}
+ */
+ function verifyAlignment(node) {
+ createGroups(node).forEach(group => {
+ verifyGroupAlignment(group.filter(isKeyValueProperty));
+ });
+ }
+
+ /**
+ * Verifies spacing of property conforms to specified options.
+ * @param {ASTNode} node Property node being evaluated.
+ * @param {Object} lineOptions Configured singleLine or multiLine options
+ * @returns {void}
+ */
+ function verifySpacing(node, lineOptions) {
+ const actual = getPropertyWhitespace(node);
+
+ if (actual) { // Object literal getters/setters lack colons
+ report(node, "key", actual.beforeColon, lineOptions.beforeColon, lineOptions.mode);
+ report(node, "value", actual.afterColon, lineOptions.afterColon, lineOptions.mode);
+ }
+ }
+
+ /**
+ * Verifies spacing of each property in a list.
+ * @param {ASTNode[]} properties List of Property AST nodes.
+ * @returns {void}
+ */
+ function verifyListSpacing(properties) {
+ const length = properties.length;
+
+ for (let i = 0; i < length; i++) {
+ verifySpacing(properties[i], singleLineOptions);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ if (alignmentOptions) { // Verify vertical alignment
+
+ return {
+ ObjectExpression(node) {
+ if (isSingleLine(node)) {
+ verifyListSpacing(node.properties.filter(isKeyValueProperty));
+ } else {
+ verifyAlignment(node);
+ }
+ }
+ };
+
+ }
+
+ // Obey beforeColon and afterColon in each property as configured
+ return {
+ Property(node) {
+ verifySpacing(node, isSingleLine(node.parent) ? singleLineOptions : multiLineOptions);
+ }
+ };
+
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/keyword-spacing.js b/tools/node_modules/eslint/lib/rules/keyword-spacing.js
new file mode 100644
index 0000000000..218cfd02be
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/keyword-spacing.js
@@ -0,0 +1,584 @@
+/**
+ * @fileoverview Rule to enforce spacing before and after keywords.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils"),
+ keywords = require("../util/keywords");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const PREV_TOKEN = /^[)\]}>]$/;
+const NEXT_TOKEN = /^(?:[([{<~!]|\+\+?|--?)$/;
+const PREV_TOKEN_M = /^[)\]}>*]$/;
+const NEXT_TOKEN_M = /^[{*]$/;
+const TEMPLATE_OPEN_PAREN = /\$\{$/;
+const TEMPLATE_CLOSE_PAREN = /^\}/;
+const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template)$/;
+const KEYS = keywords.concat(["as", "async", "await", "from", "get", "let", "of", "set", "yield"]);
+
+// check duplications.
+(function() {
+ KEYS.sort();
+ for (let i = 1; i < KEYS.length; ++i) {
+ if (KEYS[i] === KEYS[i - 1]) {
+ throw new Error(`Duplication was found in the keyword list: ${KEYS[i]}`);
+ }
+ }
+}());
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given token is a "Template" token ends with "${".
+ *
+ * @param {Token} token - A token to check.
+ * @returns {boolean} `true` if the token is a "Template" token ends with "${".
+ */
+function isOpenParenOfTemplate(token) {
+ return token.type === "Template" && TEMPLATE_OPEN_PAREN.test(token.value);
+}
+
+/**
+ * Checks whether or not a given token is a "Template" token starts with "}".
+ *
+ * @param {Token} token - A token to check.
+ * @returns {boolean} `true` if the token is a "Template" token starts with "}".
+ */
+function isCloseParenOfTemplate(token) {
+ return token.type === "Template" && TEMPLATE_CLOSE_PAREN.test(token.value);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before and after keywords",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ before: { type: "boolean" },
+ after: { type: "boolean" },
+ overrides: {
+ type: "object",
+ properties: KEYS.reduce((retv, key) => {
+ retv[key] = {
+ type: "object",
+ properties: {
+ before: { type: "boolean" },
+ after: { type: "boolean" }
+ },
+ additionalProperties: false
+ };
+ return retv;
+ }, {}),
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports a given token if there are not space(s) before the token.
+ *
+ * @param {Token} token - A token to report.
+ * @param {RegExp|undefined} pattern - Optional. A pattern of the previous
+ * token to check.
+ * @returns {void}
+ */
+ function expectSpaceBefore(token, pattern) {
+ pattern = pattern || PREV_TOKEN;
+
+ const prevToken = sourceCode.getTokenBefore(token);
+
+ if (prevToken &&
+ (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
+ !isOpenParenOfTemplate(prevToken) &&
+ astUtils.isTokenOnSameLine(prevToken, token) &&
+ !sourceCode.isSpaceBetweenTokens(prevToken, token)
+ ) {
+ context.report({
+ loc: token.loc.start,
+ message: "Expected space(s) before \"{{value}}\".",
+ data: token,
+ fix(fixer) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ });
+ }
+ }
+
+ /**
+ * Reports a given token if there are space(s) before the token.
+ *
+ * @param {Token} token - A token to report.
+ * @param {RegExp|undefined} pattern - Optional. A pattern of the previous
+ * token to check.
+ * @returns {void}
+ */
+ function unexpectSpaceBefore(token, pattern) {
+ pattern = pattern || PREV_TOKEN;
+
+ const prevToken = sourceCode.getTokenBefore(token);
+
+ if (prevToken &&
+ (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
+ !isOpenParenOfTemplate(prevToken) &&
+ astUtils.isTokenOnSameLine(prevToken, token) &&
+ sourceCode.isSpaceBetweenTokens(prevToken, token)
+ ) {
+ context.report({
+ loc: token.loc.start,
+ message: "Unexpected space(s) before \"{{value}}\".",
+ data: token,
+ fix(fixer) {
+ return fixer.removeRange([prevToken.range[1], token.range[0]]);
+ }
+ });
+ }
+ }
+
+ /**
+ * Reports a given token if there are not space(s) after the token.
+ *
+ * @param {Token} token - A token to report.
+ * @param {RegExp|undefined} pattern - Optional. A pattern of the next
+ * token to check.
+ * @returns {void}
+ */
+ function expectSpaceAfter(token, pattern) {
+ pattern = pattern || NEXT_TOKEN;
+
+ const nextToken = sourceCode.getTokenAfter(token);
+
+ if (nextToken &&
+ (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
+ !isCloseParenOfTemplate(nextToken) &&
+ astUtils.isTokenOnSameLine(token, nextToken) &&
+ !sourceCode.isSpaceBetweenTokens(token, nextToken)
+ ) {
+ context.report({
+ loc: token.loc.start,
+ message: "Expected space(s) after \"{{value}}\".",
+ data: token,
+ fix(fixer) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ });
+ }
+ }
+
+ /**
+ * Reports a given token if there are space(s) after the token.
+ *
+ * @param {Token} token - A token to report.
+ * @param {RegExp|undefined} pattern - Optional. A pattern of the next
+ * token to check.
+ * @returns {void}
+ */
+ function unexpectSpaceAfter(token, pattern) {
+ pattern = pattern || NEXT_TOKEN;
+
+ const nextToken = sourceCode.getTokenAfter(token);
+
+ if (nextToken &&
+ (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
+ !isCloseParenOfTemplate(nextToken) &&
+ astUtils.isTokenOnSameLine(token, nextToken) &&
+ sourceCode.isSpaceBetweenTokens(token, nextToken)
+ ) {
+ context.report({
+ loc: token.loc.start,
+ message: "Unexpected space(s) after \"{{value}}\".",
+ data: token,
+ fix(fixer) {
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ }
+ });
+ }
+ }
+
+ /**
+ * Parses the option object and determines check methods for each keyword.
+ *
+ * @param {Object|undefined} options - The option object to parse.
+ * @returns {Object} - Normalized option object.
+ * Keys are keywords (there are for every keyword).
+ * Values are instances of `{"before": function, "after": function}`.
+ */
+ function parseOptions(options) {
+ const before = !options || options.before !== false;
+ const after = !options || options.after !== false;
+ const defaultValue = {
+ before: before ? expectSpaceBefore : unexpectSpaceBefore,
+ after: after ? expectSpaceAfter : unexpectSpaceAfter
+ };
+ const overrides = (options && options.overrides) || {};
+ const retv = Object.create(null);
+
+ for (let i = 0; i < KEYS.length; ++i) {
+ const key = KEYS[i];
+ const override = overrides[key];
+
+ if (override) {
+ const thisBefore = ("before" in override) ? override.before : before;
+ const thisAfter = ("after" in override) ? override.after : after;
+
+ retv[key] = {
+ before: thisBefore ? expectSpaceBefore : unexpectSpaceBefore,
+ after: thisAfter ? expectSpaceAfter : unexpectSpaceAfter
+ };
+ } else {
+ retv[key] = defaultValue;
+ }
+ }
+
+ return retv;
+ }
+
+ const checkMethodMap = parseOptions(context.options[0]);
+
+ /**
+ * Reports a given token if usage of spacing followed by the token is
+ * invalid.
+ *
+ * @param {Token} token - A token to report.
+ * @param {RegExp|undefined} pattern - Optional. A pattern of the previous
+ * token to check.
+ * @returns {void}
+ */
+ function checkSpacingBefore(token, pattern) {
+ checkMethodMap[token.value].before(token, pattern);
+ }
+
+ /**
+ * Reports a given token if usage of spacing preceded by the token is
+ * invalid.
+ *
+ * @param {Token} token - A token to report.
+ * @param {RegExp|undefined} pattern - Optional. A pattern of the next
+ * token to check.
+ * @returns {void}
+ */
+ function checkSpacingAfter(token, pattern) {
+ checkMethodMap[token.value].after(token, pattern);
+ }
+
+ /**
+ * Reports a given token if usage of spacing around the token is invalid.
+ *
+ * @param {Token} token - A token to report.
+ * @returns {void}
+ */
+ function checkSpacingAround(token) {
+ checkSpacingBefore(token);
+ checkSpacingAfter(token);
+ }
+
+ /**
+ * Reports the first token of a given node if the first token is a keyword
+ * and usage of spacing around the token is invalid.
+ *
+ * @param {ASTNode|null} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingAroundFirstToken(node) {
+ const firstToken = node && sourceCode.getFirstToken(node);
+
+ if (firstToken && firstToken.type === "Keyword") {
+ checkSpacingAround(firstToken);
+ }
+ }
+
+ /**
+ * Reports the first token of a given node if the first token is a keyword
+ * and usage of spacing followed by the token is invalid.
+ *
+ * This is used for unary operators (e.g. `typeof`), `function`, and `super`.
+ * Other rules are handling usage of spacing preceded by those keywords.
+ *
+ * @param {ASTNode|null} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingBeforeFirstToken(node) {
+ const firstToken = node && sourceCode.getFirstToken(node);
+
+ if (firstToken && firstToken.type === "Keyword") {
+ checkSpacingBefore(firstToken);
+ }
+ }
+
+ /**
+ * Reports the previous token of a given node if the token is a keyword and
+ * usage of spacing around the token is invalid.
+ *
+ * @param {ASTNode|null} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingAroundTokenBefore(node) {
+ if (node) {
+ const token = sourceCode.getTokenBefore(node, astUtils.isKeywordToken);
+
+ checkSpacingAround(token);
+ }
+ }
+
+ /**
+ * Reports `async` or `function` keywords of a given node if usage of
+ * spacing around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForFunction(node) {
+ const firstToken = node && sourceCode.getFirstToken(node);
+
+ if (firstToken &&
+ ((firstToken.type === "Keyword" && firstToken.value === "function") ||
+ firstToken.value === "async")
+ ) {
+ checkSpacingBefore(firstToken);
+ }
+ }
+
+ /**
+ * Reports `class` and `extends` keywords of a given node if usage of
+ * spacing around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForClass(node) {
+ checkSpacingAroundFirstToken(node);
+ checkSpacingAroundTokenBefore(node.superClass);
+ }
+
+ /**
+ * Reports `if` and `else` keywords of a given node if usage of spacing
+ * around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForIfStatement(node) {
+ checkSpacingAroundFirstToken(node);
+ checkSpacingAroundTokenBefore(node.alternate);
+ }
+
+ /**
+ * Reports `try`, `catch`, and `finally` keywords of a given node if usage
+ * of spacing around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForTryStatement(node) {
+ checkSpacingAroundFirstToken(node);
+ checkSpacingAroundFirstToken(node.handler);
+ checkSpacingAroundTokenBefore(node.finalizer);
+ }
+
+ /**
+ * Reports `do` and `while` keywords of a given node if usage of spacing
+ * around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForDoWhileStatement(node) {
+ checkSpacingAroundFirstToken(node);
+ checkSpacingAroundTokenBefore(node.test);
+ }
+
+ /**
+ * Reports `for` and `in` keywords of a given node if usage of spacing
+ * around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForForInStatement(node) {
+ checkSpacingAroundFirstToken(node);
+ checkSpacingAroundTokenBefore(node.right);
+ }
+
+ /**
+ * Reports `for` and `of` keywords of a given node if usage of spacing
+ * around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForForOfStatement(node) {
+ checkSpacingAroundFirstToken(node);
+ checkSpacingAround(sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken));
+ }
+
+ /**
+ * Reports `import`, `export`, `as`, and `from` keywords of a given node if
+ * usage of spacing around those keywords is invalid.
+ *
+ * This rule handles the `*` token in module declarations.
+ *
+ * import*as A from "./a"; /*error Expected space(s) after "import".
+ * error Expected space(s) before "as".
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForModuleDeclaration(node) {
+ const firstToken = sourceCode.getFirstToken(node);
+
+ checkSpacingBefore(firstToken, PREV_TOKEN_M);
+ checkSpacingAfter(firstToken, NEXT_TOKEN_M);
+
+ if (node.source) {
+ const fromToken = sourceCode.getTokenBefore(node.source);
+
+ checkSpacingBefore(fromToken, PREV_TOKEN_M);
+ checkSpacingAfter(fromToken, NEXT_TOKEN_M);
+ }
+ }
+
+ /**
+ * Reports `as` keyword of a given node if usage of spacing around this
+ * keyword is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForImportNamespaceSpecifier(node) {
+ const asToken = sourceCode.getFirstToken(node, 1);
+
+ checkSpacingBefore(asToken, PREV_TOKEN_M);
+ }
+
+ /**
+ * Reports `static`, `get`, and `set` keywords of a given node if usage of
+ * spacing around those keywords is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForProperty(node) {
+ if (node.static) {
+ checkSpacingAroundFirstToken(node);
+ }
+ if (node.kind === "get" ||
+ node.kind === "set" ||
+ (
+ (node.method || node.type === "MethodDefinition") &&
+ node.value.async
+ )
+ ) {
+ const token = sourceCode.getTokenBefore(
+ node.key,
+ tok => {
+ switch (tok.value) {
+ case "get":
+ case "set":
+ case "async":
+ return true;
+ default:
+ return false;
+ }
+ }
+ );
+
+ if (!token) {
+ throw new Error("Failed to find token get, set, or async beside method name");
+ }
+
+
+ checkSpacingAround(token);
+ }
+ }
+
+ /**
+ * Reports `await` keyword of a given node if usage of spacing before
+ * this keyword is invalid.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function checkSpacingForAwaitExpression(node) {
+ checkSpacingBefore(sourceCode.getFirstToken(node));
+ }
+
+ return {
+
+ // Statements
+ DebuggerStatement: checkSpacingAroundFirstToken,
+ WithStatement: checkSpacingAroundFirstToken,
+
+ // Statements - Control flow
+ BreakStatement: checkSpacingAroundFirstToken,
+ ContinueStatement: checkSpacingAroundFirstToken,
+ ReturnStatement: checkSpacingAroundFirstToken,
+ ThrowStatement: checkSpacingAroundFirstToken,
+ TryStatement: checkSpacingForTryStatement,
+
+ // Statements - Choice
+ IfStatement: checkSpacingForIfStatement,
+ SwitchStatement: checkSpacingAroundFirstToken,
+ SwitchCase: checkSpacingAroundFirstToken,
+
+ // Statements - Loops
+ DoWhileStatement: checkSpacingForDoWhileStatement,
+ ForInStatement: checkSpacingForForInStatement,
+ ForOfStatement: checkSpacingForForOfStatement,
+ ForStatement: checkSpacingAroundFirstToken,
+ WhileStatement: checkSpacingAroundFirstToken,
+
+ // Statements - Declarations
+ ClassDeclaration: checkSpacingForClass,
+ ExportNamedDeclaration: checkSpacingForModuleDeclaration,
+ ExportDefaultDeclaration: checkSpacingAroundFirstToken,
+ ExportAllDeclaration: checkSpacingForModuleDeclaration,
+ FunctionDeclaration: checkSpacingForFunction,
+ ImportDeclaration: checkSpacingForModuleDeclaration,
+ VariableDeclaration: checkSpacingAroundFirstToken,
+
+ // Expressions
+ ArrowFunctionExpression: checkSpacingForFunction,
+ AwaitExpression: checkSpacingForAwaitExpression,
+ ClassExpression: checkSpacingForClass,
+ FunctionExpression: checkSpacingForFunction,
+ NewExpression: checkSpacingBeforeFirstToken,
+ Super: checkSpacingBeforeFirstToken,
+ ThisExpression: checkSpacingBeforeFirstToken,
+ UnaryExpression: checkSpacingBeforeFirstToken,
+ YieldExpression: checkSpacingBeforeFirstToken,
+
+ // Others
+ ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
+ MethodDefinition: checkSpacingForProperty,
+ Property: checkSpacingForProperty
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/line-comment-position.js b/tools/node_modules/eslint/lib/rules/line-comment-position.js
new file mode 100644
index 0000000000..0df806cca8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/line-comment-position.js
@@ -0,0 +1,115 @@
+/**
+ * @fileoverview Rule to enforce the position of line comments
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce position of line comments",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["above", "beside"]
+ },
+ {
+ type: "object",
+ properties: {
+ position: {
+ enum: ["above", "beside"]
+ },
+ ignorePattern: {
+ type: "string"
+ },
+ applyDefaultPatterns: {
+ type: "boolean"
+ },
+ applyDefaultIgnorePatterns: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0];
+
+ let above,
+ ignorePattern,
+ applyDefaultIgnorePatterns = true;
+
+ if (!options || typeof options === "string") {
+ above = !options || options === "above";
+
+ } else {
+ above = options.position === "above";
+ ignorePattern = options.ignorePattern;
+
+ if (options.hasOwnProperty("applyDefaultIgnorePatterns")) {
+ applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
+ } else {
+ applyDefaultIgnorePatterns = options.applyDefaultPatterns !== false;
+ }
+ }
+
+ const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
+ const fallThroughRegExp = /^\s*falls?\s?through/;
+ const customIgnoreRegExp = new RegExp(ignorePattern);
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program() {
+ const comments = sourceCode.getAllComments();
+
+ comments.filter(token => token.type === "Line").forEach(node => {
+ if (applyDefaultIgnorePatterns && (defaultIgnoreRegExp.test(node.value) || fallThroughRegExp.test(node.value))) {
+ return;
+ }
+
+ if (ignorePattern && customIgnoreRegExp.test(node.value)) {
+ return;
+ }
+
+ const previous = sourceCode.getTokenBefore(node, { includeComments: true });
+ const isOnSameLine = previous && previous.loc.end.line === node.loc.start.line;
+
+ if (above) {
+ if (isOnSameLine) {
+ context.report({
+ node,
+ message: "Expected comment to be above code."
+ });
+ }
+ } else {
+ if (!isOnSameLine) {
+ context.report({
+ node,
+ message: "Expected comment to be beside code."
+ });
+ }
+ }
+ });
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/linebreak-style.js b/tools/node_modules/eslint/lib/rules/linebreak-style.js
new file mode 100644
index 0000000000..907bc02ec4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/linebreak-style.js
@@ -0,0 +1,96 @@
+/**
+ * @fileoverview Rule to enforce a single linebreak style.
+ * @author Erik Mueller
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent linebreak style",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["unix", "windows"]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const EXPECTED_LF_MSG = "Expected linebreaks to be 'LF' but found 'CRLF'.",
+ EXPECTED_CRLF_MSG = "Expected linebreaks to be 'CRLF' but found 'LF'.";
+
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Builds a fix function that replaces text at the specified range in the source text.
+ * @param {int[]} range The range to replace
+ * @param {string} text The text to insert.
+ * @returns {Function} Fixer function
+ * @private
+ */
+ function createFix(range, text) {
+ return function(fixer) {
+ return fixer.replaceTextRange(range, text);
+ };
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: function checkForlinebreakStyle(node) {
+ const linebreakStyle = context.options[0] || "unix",
+ expectedLF = linebreakStyle === "unix",
+ expectedLFChars = expectedLF ? "\n" : "\r\n",
+ source = sourceCode.getText(),
+ pattern = astUtils.createGlobalLinebreakMatcher();
+ let match;
+
+ let i = 0;
+
+ while ((match = pattern.exec(source)) !== null) {
+ i++;
+ if (match[0] === expectedLFChars) {
+ continue;
+ }
+
+ const index = match.index;
+ const range = [index, index + match[0].length];
+
+ context.report({
+ node,
+ loc: {
+ line: i,
+ column: sourceCode.lines[i - 1].length
+ },
+ message: expectedLF ? EXPECTED_LF_MSG : EXPECTED_CRLF_MSG,
+ fix: createFix(range, expectedLFChars)
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/lines-around-comment.js b/tools/node_modules/eslint/lib/rules/lines-around-comment.js
new file mode 100644
index 0000000000..3df9cb86f4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/lines-around-comment.js
@@ -0,0 +1,397 @@
+/**
+ * @fileoverview Enforces empty lines around comments.
+ * @author Jamund Ferguson
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash"),
+ astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Return an array with with any line numbers that are empty.
+ * @param {Array} lines An array of each line of the file.
+ * @returns {Array} An array of line numbers.
+ */
+function getEmptyLineNums(lines) {
+ const emptyLines = lines.map((line, i) => ({
+ code: line.trim(),
+ num: i + 1
+ })).filter(line => !line.code).map(line => line.num);
+
+ return emptyLines;
+}
+
+/**
+ * Return an array with with any line numbers that contain comments.
+ * @param {Array} comments An array of comment tokens.
+ * @returns {Array} An array of line numbers.
+ */
+function getCommentLineNums(comments) {
+ const lines = [];
+
+ comments.forEach(token => {
+ const start = token.loc.start.line;
+ const end = token.loc.end.line;
+
+ lines.push(start, end);
+ });
+ return lines;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require empty lines around comments",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ beforeBlockComment: {
+ type: "boolean"
+ },
+ afterBlockComment: {
+ type: "boolean"
+ },
+ beforeLineComment: {
+ type: "boolean"
+ },
+ afterLineComment: {
+ type: "boolean"
+ },
+ allowBlockStart: {
+ type: "boolean"
+ },
+ allowBlockEnd: {
+ type: "boolean"
+ },
+ allowClassStart: {
+ type: "boolean"
+ },
+ allowClassEnd: {
+ type: "boolean"
+ },
+ allowObjectStart: {
+ type: "boolean"
+ },
+ allowObjectEnd: {
+ type: "boolean"
+ },
+ allowArrayStart: {
+ type: "boolean"
+ },
+ allowArrayEnd: {
+ type: "boolean"
+ },
+ ignorePattern: {
+ type: "string"
+ },
+ applyDefaultIgnorePatterns: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = context.options[0] ? Object.assign({}, context.options[0]) : {};
+ const ignorePattern = options.ignorePattern;
+ const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
+ const customIgnoreRegExp = new RegExp(ignorePattern);
+ const applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
+
+
+ options.beforeLineComment = options.beforeLineComment || false;
+ options.afterLineComment = options.afterLineComment || false;
+ options.beforeBlockComment = typeof options.beforeBlockComment !== "undefined" ? options.beforeBlockComment : true;
+ options.afterBlockComment = options.afterBlockComment || false;
+ options.allowBlockStart = options.allowBlockStart || false;
+ options.allowBlockEnd = options.allowBlockEnd || false;
+
+ const sourceCode = context.getSourceCode();
+
+ const lines = sourceCode.lines,
+ numLines = lines.length + 1,
+ comments = sourceCode.getAllComments(),
+ commentLines = getCommentLineNums(comments),
+ emptyLines = getEmptyLineNums(lines),
+ commentAndEmptyLines = commentLines.concat(emptyLines);
+
+ /**
+ * Returns whether or not comments are on lines starting with or ending with code
+ * @param {token} token The comment token to check.
+ * @returns {boolean} True if the comment is not alone.
+ */
+ function codeAroundComment(token) {
+ let currentToken = token;
+
+ do {
+ currentToken = sourceCode.getTokenBefore(currentToken, { includeComments: true });
+ } while (currentToken && astUtils.isCommentToken(currentToken));
+
+ if (currentToken && astUtils.isTokenOnSameLine(currentToken, token)) {
+ return true;
+ }
+
+ currentToken = token;
+ do {
+ currentToken = sourceCode.getTokenAfter(currentToken, { includeComments: true });
+ } while (currentToken && astUtils.isCommentToken(currentToken));
+
+ if (currentToken && astUtils.isTokenOnSameLine(token, currentToken)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether or not comments are inside a node type or not.
+ * @param {ASTNode} parent The Comment parent node.
+ * @param {string} nodeType The parent type to check against.
+ * @returns {boolean} True if the comment is inside nodeType.
+ */
+ function isParentNodeType(parent, nodeType) {
+ return parent.type === nodeType ||
+ (parent.body && parent.body.type === nodeType) ||
+ (parent.consequent && parent.consequent.type === nodeType);
+ }
+
+ /**
+ * Returns the parent node that contains the given token.
+ * @param {token} token The token to check.
+ * @returns {ASTNode} The parent node that contains the given token.
+ */
+ function getParentNodeOfToken(token) {
+ return sourceCode.getNodeByRangeIndex(token.range[0]);
+ }
+
+ /**
+ * Returns whether or not comments are at the parent start or not.
+ * @param {token} token The Comment token.
+ * @param {string} nodeType The parent type to check against.
+ * @returns {boolean} True if the comment is at parent start.
+ */
+ function isCommentAtParentStart(token, nodeType) {
+ const parent = getParentNodeOfToken(token);
+
+ return parent && isParentNodeType(parent, nodeType) &&
+ token.loc.start.line - parent.loc.start.line === 1;
+ }
+
+ /**
+ * Returns whether or not comments are at the parent end or not.
+ * @param {token} token The Comment token.
+ * @param {string} nodeType The parent type to check against.
+ * @returns {boolean} True if the comment is at parent end.
+ */
+ function isCommentAtParentEnd(token, nodeType) {
+ const parent = getParentNodeOfToken(token);
+
+ return parent && isParentNodeType(parent, nodeType) &&
+ parent.loc.end.line - token.loc.end.line === 1;
+ }
+
+ /**
+ * Returns whether or not comments are at the block start or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at block start.
+ */
+ function isCommentAtBlockStart(token) {
+ return isCommentAtParentStart(token, "ClassBody") || isCommentAtParentStart(token, "BlockStatement") || isCommentAtParentStart(token, "SwitchCase");
+ }
+
+ /**
+ * Returns whether or not comments are at the block end or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at block end.
+ */
+ function isCommentAtBlockEnd(token) {
+ return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
+ }
+
+ /**
+ * Returns whether or not comments are at the class start or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at class start.
+ */
+ function isCommentAtClassStart(token) {
+ return isCommentAtParentStart(token, "ClassBody");
+ }
+
+ /**
+ * Returns whether or not comments are at the class end or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at class end.
+ */
+ function isCommentAtClassEnd(token) {
+ return isCommentAtParentEnd(token, "ClassBody");
+ }
+
+ /**
+ * Returns whether or not comments are at the object start or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at object start.
+ */
+ function isCommentAtObjectStart(token) {
+ return isCommentAtParentStart(token, "ObjectExpression") || isCommentAtParentStart(token, "ObjectPattern");
+ }
+
+ /**
+ * Returns whether or not comments are at the object end or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at object end.
+ */
+ function isCommentAtObjectEnd(token) {
+ return isCommentAtParentEnd(token, "ObjectExpression") || isCommentAtParentEnd(token, "ObjectPattern");
+ }
+
+ /**
+ * Returns whether or not comments are at the array start or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at array start.
+ */
+ function isCommentAtArrayStart(token) {
+ return isCommentAtParentStart(token, "ArrayExpression") || isCommentAtParentStart(token, "ArrayPattern");
+ }
+
+ /**
+ * Returns whether or not comments are at the array end or not.
+ * @param {token} token The Comment token.
+ * @returns {boolean} True if the comment is at array end.
+ */
+ function isCommentAtArrayEnd(token) {
+ return isCommentAtParentEnd(token, "ArrayExpression") || isCommentAtParentEnd(token, "ArrayPattern");
+ }
+
+ /**
+ * Checks if a comment token has lines around it (ignores inline comments)
+ * @param {token} token The Comment token.
+ * @param {Object} opts Options to determine the newline.
+ * @param {boolean} opts.after Should have a newline after this line.
+ * @param {boolean} opts.before Should have a newline before this line.
+ * @returns {void}
+ */
+ function checkForEmptyLine(token, opts) {
+ if (applyDefaultIgnorePatterns && defaultIgnoreRegExp.test(token.value)) {
+ return;
+ }
+
+ if (ignorePattern && customIgnoreRegExp.test(token.value)) {
+ return;
+ }
+
+ let after = opts.after,
+ before = opts.before;
+
+ const prevLineNum = token.loc.start.line - 1,
+ nextLineNum = token.loc.end.line + 1,
+ commentIsNotAlone = codeAroundComment(token);
+
+ const blockStartAllowed = options.allowBlockStart &&
+ isCommentAtBlockStart(token) &&
+ !(options.allowClassStart === false &&
+ isCommentAtClassStart(token)),
+ blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
+ classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
+ classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
+ objectStartAllowed = options.allowObjectStart && isCommentAtObjectStart(token),
+ objectEndAllowed = options.allowObjectEnd && isCommentAtObjectEnd(token),
+ arrayStartAllowed = options.allowArrayStart && isCommentAtArrayStart(token),
+ arrayEndAllowed = options.allowArrayEnd && isCommentAtArrayEnd(token);
+
+ const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed;
+ const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed;
+
+ // ignore top of the file and bottom of the file
+ if (prevLineNum < 1) {
+ before = false;
+ }
+ if (nextLineNum >= numLines) {
+ after = false;
+ }
+
+ // we ignore all inline comments
+ if (commentIsNotAlone) {
+ return;
+ }
+
+ const previousTokenOrComment = sourceCode.getTokenBefore(token, { includeComments: true });
+ const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
+
+ // check for newline before
+ if (!exceptionStartAllowed && before && !lodash.includes(commentAndEmptyLines, prevLineNum) &&
+ !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {
+ const lineStart = token.range[0] - token.loc.start.column;
+ const range = [lineStart, lineStart];
+
+ context.report({
+ node: token,
+ message: "Expected line before comment.",
+ fix(fixer) {
+ return fixer.insertTextBeforeRange(range, "\n");
+ }
+ });
+ }
+
+ // check for newline after
+ if (!exceptionEndAllowed && after && !lodash.includes(commentAndEmptyLines, nextLineNum) &&
+ !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {
+ context.report({
+ node: token,
+ message: "Expected line after comment.",
+ fix(fixer) {
+ return fixer.insertTextAfter(token, "\n");
+ }
+ });
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program() {
+ comments.forEach(token => {
+ if (token.type === "Line") {
+ if (options.beforeLineComment || options.afterLineComment) {
+ checkForEmptyLine(token, {
+ after: options.afterLineComment,
+ before: options.beforeLineComment
+ });
+ }
+ } else if (token.type === "Block") {
+ if (options.beforeBlockComment || options.afterBlockComment) {
+ checkForEmptyLine(token, {
+ after: options.afterBlockComment,
+ before: options.beforeBlockComment
+ });
+ }
+ }
+ });
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/lines-around-directive.js b/tools/node_modules/eslint/lib/rules/lines-around-directive.js
new file mode 100644
index 0000000000..b560009f71
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/lines-around-directive.js
@@ -0,0 +1,193 @@
+/**
+ * @fileoverview Require or disallow newlines around directives.
+ * @author Kai Cataldo
+ * @deprecated
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow newlines around directives",
+ category: "Stylistic Issues",
+ recommended: false,
+ replacedBy: ["padding-line-between-statements"]
+ },
+ schema: [{
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ before: {
+ enum: ["always", "never"]
+ },
+ after: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false,
+ minProperties: 2
+ }
+ ]
+ }],
+ fixable: "whitespace",
+ deprecated: true
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const config = context.options[0] || "always";
+ const expectLineBefore = typeof config === "string" ? config : config.before;
+ const expectLineAfter = typeof config === "string" ? config : config.after;
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Check if node is preceded by a blank newline.
+ * @param {ASTNode} node Node to check.
+ * @returns {boolean} Whether or not the passed in node is preceded by a blank newline.
+ */
+ function hasNewlineBefore(node) {
+ const tokenBefore = sourceCode.getTokenBefore(node, { includeComments: true });
+ const tokenLineBefore = tokenBefore ? tokenBefore.loc.end.line : 0;
+
+ return node.loc.start.line - tokenLineBefore >= 2;
+ }
+
+ /**
+ * Gets the last token of a node that is on the same line as the rest of the node.
+ * This will usually be the last token of the node, but it will be the second-to-last token if the node has a trailing
+ * semicolon on a different line.
+ * @param {ASTNode} node A directive node
+ * @returns {Token} The last token of the node on the line
+ */
+ function getLastTokenOnLine(node) {
+ const lastToken = sourceCode.getLastToken(node);
+ const secondToLastToken = sourceCode.getTokenBefore(lastToken);
+
+ return astUtils.isSemicolonToken(lastToken) && lastToken.loc.start.line > secondToLastToken.loc.end.line
+ ? secondToLastToken
+ : lastToken;
+ }
+
+ /**
+ * Check if node is followed by a blank newline.
+ * @param {ASTNode} node Node to check.
+ * @returns {boolean} Whether or not the passed in node is followed by a blank newline.
+ */
+ function hasNewlineAfter(node) {
+ const lastToken = getLastTokenOnLine(node);
+ const tokenAfter = sourceCode.getTokenAfter(lastToken, { includeComments: true });
+
+ return tokenAfter.loc.start.line - lastToken.loc.end.line >= 2;
+ }
+
+ /**
+ * Report errors for newlines around directives.
+ * @param {ASTNode} node Node to check.
+ * @param {string} location Whether the error was found before or after the directive.
+ * @param {boolean} expected Whether or not a newline was expected or unexpected.
+ * @returns {void}
+ */
+ function reportError(node, location, expected) {
+ context.report({
+ node,
+ message: "{{expected}} newline {{location}} \"{{value}}\" directive.",
+ data: {
+ expected: expected ? "Expected" : "Unexpected",
+ value: node.expression.value,
+ location
+ },
+ fix(fixer) {
+ const lastToken = getLastTokenOnLine(node);
+
+ if (expected) {
+ return location === "before" ? fixer.insertTextBefore(node, "\n") : fixer.insertTextAfter(lastToken, "\n");
+ }
+ return fixer.removeRange(location === "before" ? [node.range[0] - 1, node.range[0]] : [lastToken.range[1], lastToken.range[1] + 1]);
+ }
+ });
+ }
+
+ /**
+ * Check lines around directives in node
+ * @param {ASTNode} node - node to check
+ * @returns {void}
+ */
+ function checkDirectives(node) {
+ const directives = astUtils.getDirectivePrologue(node);
+
+ if (!directives.length) {
+ return;
+ }
+
+ const firstDirective = directives[0];
+ const leadingComments = sourceCode.getCommentsBefore(firstDirective);
+
+ /*
+ * Only check before the first directive if it is preceded by a comment or if it is at the top of
+ * the file and expectLineBefore is set to "never". This is to not force a newline at the top of
+ * the file if there are no comments as well as for compatibility with padded-blocks.
+ */
+ if (leadingComments.length) {
+ if (expectLineBefore === "always" && !hasNewlineBefore(firstDirective)) {
+ reportError(firstDirective, "before", true);
+ }
+
+ if (expectLineBefore === "never" && hasNewlineBefore(firstDirective)) {
+ reportError(firstDirective, "before", false);
+ }
+ } else if (
+ node.type === "Program" &&
+ expectLineBefore === "never" &&
+ !leadingComments.length &&
+ hasNewlineBefore(firstDirective)
+ ) {
+ reportError(firstDirective, "before", false);
+ }
+
+ const lastDirective = directives[directives.length - 1];
+ const statements = node.type === "Program" ? node.body : node.body.body;
+
+ /*
+ * Do not check after the last directive if the body only
+ * contains a directive prologue and isn't followed by a comment to ensure
+ * this rule behaves well with padded-blocks.
+ */
+ if (lastDirective === statements[statements.length - 1] && !lastDirective.trailingComments) {
+ return;
+ }
+
+ if (expectLineAfter === "always" && !hasNewlineAfter(lastDirective)) {
+ reportError(lastDirective, "after", true);
+ }
+
+ if (expectLineAfter === "never" && hasNewlineAfter(lastDirective)) {
+ reportError(lastDirective, "after", false);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: checkDirectives,
+ FunctionDeclaration: checkDirectives,
+ FunctionExpression: checkDirectives,
+ ArrowFunctionExpression: checkDirectives
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/lines-between-class-members.js b/tools/node_modules/eslint/lib/rules/lines-between-class-members.js
new file mode 100644
index 0000000000..85e8c69358
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/lines-between-class-members.js
@@ -0,0 +1,91 @@
+/**
+ * @fileoverview Rule to check empty newline between class members
+ * @author 薛定谔的猫<hh_2013@foxmail.com>
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow an empty line between class members",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ exceptAfterSingleLine: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = [];
+
+ options[0] = context.options[0] || "always";
+ options[1] = context.options[1] || { exceptAfterSingleLine: false };
+
+ const ALWAYS_MESSAGE = "Expected blank line between class members.";
+ const NEVER_MESSAGE = "Unexpected blank line between class members.";
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Checks if there is padding between two tokens
+ * @param {Token} first The first token
+ * @param {Token} second The second token
+ * @returns {boolean} True if there is at least a line between the tokens
+ */
+ function isPaddingBetweenTokens(first, second) {
+ return second.loc.start.line - first.loc.end.line >= 2;
+ }
+
+ return {
+ ClassBody(node) {
+ const body = node.body;
+
+ for (let i = 0; i < body.length - 1; i++) {
+ const curFirst = sourceCode.getFirstToken(body[i]);
+ const curLast = sourceCode.getLastToken(body[i]);
+ const comments = sourceCode.getCommentsBefore(body[i + 1]);
+ const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]);
+ const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
+ const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
+ const skip = !isMulti && options[1].exceptAfterSingleLine;
+
+
+ if ((options[0] === "always" && !skip && !isPadded) ||
+ (options[0] === "never" && isPadded)) {
+ context.report({
+ node: body[i + 1],
+ message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE,
+ fix(fixer) {
+ return isPadded
+ ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
+ : fixer.insertTextAfter(curLast, "\n");
+ }
+ });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-depth.js b/tools/node_modules/eslint/lib/rules/max-depth.js
new file mode 100644
index 0000000000..74c13ffa9f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-depth.js
@@ -0,0 +1,148 @@
+/**
+ * @fileoverview A rule to set the maximum depth block can be nested in a function.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum depth that blocks can be nested",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ maximum: {
+ type: "integer",
+ minimum: 0
+ },
+ max: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const functionStack = [],
+ option = context.options[0];
+ let maxDepth = 4;
+
+ if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
+ maxDepth = option.maximum;
+ }
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
+ maxDepth = option.max;
+ }
+ if (typeof option === "number") {
+ maxDepth = option;
+ }
+
+ /**
+ * When parsing a new function, store it in our function stack
+ * @returns {void}
+ * @private
+ */
+ function startFunction() {
+ functionStack.push(0);
+ }
+
+ /**
+ * When parsing is done then pop out the reference
+ * @returns {void}
+ * @private
+ */
+ function endFunction() {
+ functionStack.pop();
+ }
+
+ /**
+ * Save the block and Evaluate the node
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function pushBlock(node) {
+ const len = ++functionStack[functionStack.length - 1];
+
+ if (len > maxDepth) {
+ context.report({ node, message: "Blocks are nested too deeply ({{depth}}).", data: { depth: len } });
+ }
+ }
+
+ /**
+ * Pop the saved block
+ * @returns {void}
+ * @private
+ */
+ function popBlock() {
+ functionStack[functionStack.length - 1]--;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: startFunction,
+ FunctionDeclaration: startFunction,
+ FunctionExpression: startFunction,
+ ArrowFunctionExpression: startFunction,
+
+ IfStatement(node) {
+ if (node.parent.type !== "IfStatement") {
+ pushBlock(node);
+ }
+ },
+ SwitchStatement: pushBlock,
+ TryStatement: pushBlock,
+ DoWhileStatement: pushBlock,
+ WhileStatement: pushBlock,
+ WithStatement: pushBlock,
+ ForStatement: pushBlock,
+ ForInStatement: pushBlock,
+ ForOfStatement: pushBlock,
+
+ "IfStatement:exit": popBlock,
+ "SwitchStatement:exit": popBlock,
+ "TryStatement:exit": popBlock,
+ "DoWhileStatement:exit": popBlock,
+ "WhileStatement:exit": popBlock,
+ "WithStatement:exit": popBlock,
+ "ForStatement:exit": popBlock,
+ "ForInStatement:exit": popBlock,
+ "ForOfStatement:exit": popBlock,
+
+ "FunctionDeclaration:exit": endFunction,
+ "FunctionExpression:exit": endFunction,
+ "ArrowFunctionExpression:exit": endFunction,
+ "Program:exit": endFunction
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-len.js b/tools/node_modules/eslint/lib/rules/max-len.js
new file mode 100644
index 0000000000..27d549c9c5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-len.js
@@ -0,0 +1,365 @@
+/**
+ * @fileoverview Rule to check for max length on a line.
+ * @author Matt DuVall <http://www.mattduvall.com>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const OPTIONS_SCHEMA = {
+ type: "object",
+ properties: {
+ code: {
+ type: "integer",
+ minimum: 0
+ },
+ comments: {
+ type: "integer",
+ minimum: 0
+ },
+ tabWidth: {
+ type: "integer",
+ minimum: 0
+ },
+ ignorePattern: {
+ type: "string"
+ },
+ ignoreComments: {
+ type: "boolean"
+ },
+ ignoreStrings: {
+ type: "boolean"
+ },
+ ignoreUrls: {
+ type: "boolean"
+ },
+ ignoreTemplateLiterals: {
+ type: "boolean"
+ },
+ ignoreRegExpLiterals: {
+ type: "boolean"
+ },
+ ignoreTrailingComments: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+};
+
+const OPTIONS_OR_INTEGER_SCHEMA = {
+ anyOf: [
+ OPTIONS_SCHEMA,
+ {
+ type: "integer",
+ minimum: 0
+ }
+ ]
+};
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum line length",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ OPTIONS_OR_INTEGER_SCHEMA,
+ OPTIONS_OR_INTEGER_SCHEMA,
+ OPTIONS_SCHEMA
+ ]
+ },
+
+ create(context) {
+
+ /*
+ * Inspired by http://tools.ietf.org/html/rfc3986#appendix-B, however:
+ * - They're matching an entire string that we know is a URI
+ * - We're matching part of a string where we think there *might* be a URL
+ * - We're only concerned about URLs, as picking out any URI would cause
+ * too many false positives
+ * - We don't care about matching the entire URL, any small segment is fine
+ */
+ const URL_REGEXP = /[^:/?#]:\/\/[^?#]/;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Computes the length of a line that may contain tabs. The width of each
+ * tab will be the number of spaces to the next tab stop.
+ * @param {string} line The line.
+ * @param {int} tabWidth The width of each tab stop in spaces.
+ * @returns {int} The computed line length.
+ * @private
+ */
+ function computeLineLength(line, tabWidth) {
+ let extraCharacterCount = 0;
+
+ line.replace(/\t/g, (match, offset) => {
+ const totalOffset = offset + extraCharacterCount,
+ previousTabStopOffset = tabWidth ? totalOffset % tabWidth : 0,
+ spaceCount = tabWidth - previousTabStopOffset;
+
+ extraCharacterCount += spaceCount - 1; // -1 for the replaced tab
+ });
+ return Array.from(line).length + extraCharacterCount;
+ }
+
+ // The options object must be the last option specified…
+ const lastOption = context.options[context.options.length - 1];
+ const options = typeof lastOption === "object" ? Object.create(lastOption) : {};
+
+ // …but max code length…
+ if (typeof context.options[0] === "number") {
+ options.code = context.options[0];
+ }
+
+ // …and tabWidth can be optionally specified directly as integers.
+ if (typeof context.options[1] === "number") {
+ options.tabWidth = context.options[1];
+ }
+
+ const maxLength = options.code || 80,
+ tabWidth = options.tabWidth || 4,
+ ignoreComments = options.ignoreComments || false,
+ ignoreStrings = options.ignoreStrings || false,
+ ignoreTemplateLiterals = options.ignoreTemplateLiterals || false,
+ ignoreRegExpLiterals = options.ignoreRegExpLiterals || false,
+ ignoreTrailingComments = options.ignoreTrailingComments || options.ignoreComments || false,
+ ignoreUrls = options.ignoreUrls || false,
+ maxCommentLength = options.comments;
+ let ignorePattern = options.ignorePattern || null;
+
+ if (ignorePattern) {
+ ignorePattern = new RegExp(ignorePattern);
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Tells if a given comment is trailing: it starts on the current line and
+ * extends to or past the end of the current line.
+ * @param {string} line The source line we want to check for a trailing comment on
+ * @param {number} lineNumber The one-indexed line number for line
+ * @param {ASTNode} comment The comment to inspect
+ * @returns {boolean} If the comment is trailing on the given line
+ */
+ function isTrailingComment(line, lineNumber, comment) {
+ return comment &&
+ (comment.loc.start.line === lineNumber && lineNumber <= comment.loc.end.line) &&
+ (comment.loc.end.line > lineNumber || comment.loc.end.column === line.length);
+ }
+
+ /**
+ * Tells if a comment encompasses the entire line.
+ * @param {string} line The source line with a trailing comment
+ * @param {number} lineNumber The one-indexed line number this is on
+ * @param {ASTNode} comment The comment to remove
+ * @returns {boolean} If the comment covers the entire line
+ */
+ function isFullLineComment(line, lineNumber, comment) {
+ const start = comment.loc.start,
+ end = comment.loc.end,
+ isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim();
+
+ return comment &&
+ (start.line < lineNumber || (start.line === lineNumber && isFirstTokenOnLine)) &&
+ (end.line > lineNumber || (end.line === lineNumber && end.column === line.length));
+ }
+
+ /**
+ * Gets the line after the comment and any remaining trailing whitespace is
+ * stripped.
+ * @param {string} line The source line with a trailing comment
+ * @param {ASTNode} comment The comment to remove
+ * @returns {string} Line without comment and trailing whitepace
+ */
+ function stripTrailingComment(line, comment) {
+
+ // loc.column is zero-indexed
+ return line.slice(0, comment.loc.start.column).replace(/\s+$/, "");
+ }
+
+ /**
+ * Ensure that an array exists at [key] on `object`, and add `value` to it.
+ *
+ * @param {Object} object the object to mutate
+ * @param {string} key the object's key
+ * @param {*} value the value to add
+ * @returns {void}
+ * @private
+ */
+ function ensureArrayAndPush(object, key, value) {
+ if (!Array.isArray(object[key])) {
+ object[key] = [];
+ }
+ object[key].push(value);
+ }
+
+ /**
+ * Retrieves an array containing all strings (" or ') in the source code.
+ *
+ * @returns {ASTNode[]} An array of string nodes.
+ */
+ function getAllStrings() {
+ return sourceCode.ast.tokens.filter(token => token.type === "String");
+ }
+
+ /**
+ * Retrieves an array containing all template literals in the source code.
+ *
+ * @returns {ASTNode[]} An array of template literal nodes.
+ */
+ function getAllTemplateLiterals() {
+ return sourceCode.ast.tokens.filter(token => token.type === "Template");
+ }
+
+
+ /**
+ * Retrieves an array containing all RegExp literals in the source code.
+ *
+ * @returns {ASTNode[]} An array of RegExp literal nodes.
+ */
+ function getAllRegExpLiterals() {
+ return sourceCode.ast.tokens.filter(token => token.type === "RegularExpression");
+ }
+
+
+ /**
+ * A reducer to group an AST node by line number, both start and end.
+ *
+ * @param {Object} acc the accumulator
+ * @param {ASTNode} node the AST node in question
+ * @returns {Object} the modified accumulator
+ * @private
+ */
+ function groupByLineNumber(acc, node) {
+ for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) {
+ ensureArrayAndPush(acc, i, node);
+ }
+ return acc;
+ }
+
+ /**
+ * Check the program for max length
+ * @param {ASTNode} node Node to examine
+ * @returns {void}
+ * @private
+ */
+ function checkProgramForMaxLength(node) {
+
+ // split (honors line-ending)
+ const lines = sourceCode.lines,
+
+ // list of comments to ignore
+ comments = ignoreComments || maxCommentLength || ignoreTrailingComments ? sourceCode.getAllComments() : [];
+
+ // we iterate over comments in parallel with the lines
+ let commentsIndex = 0;
+
+ const strings = getAllStrings();
+ const stringsByLine = strings.reduce(groupByLineNumber, {});
+
+ const templateLiterals = getAllTemplateLiterals();
+ const templateLiteralsByLine = templateLiterals.reduce(groupByLineNumber, {});
+
+ const regExpLiterals = getAllRegExpLiterals();
+ const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {});
+
+ lines.forEach((line, i) => {
+
+ // i is zero-indexed, line numbers are one-indexed
+ const lineNumber = i + 1;
+
+ /*
+ * if we're checking comment length; we need to know whether this
+ * line is a comment
+ */
+ let lineIsComment = false;
+
+ /*
+ * We can short-circuit the comment checks if we're already out of
+ * comments to check.
+ */
+ if (commentsIndex < comments.length) {
+ let comment = null;
+
+ // iterate over comments until we find one past the current line
+ do {
+ comment = comments[++commentsIndex];
+ } while (comment && comment.loc.start.line <= lineNumber);
+
+ // and step back by one
+ comment = comments[--commentsIndex];
+
+ if (isFullLineComment(line, lineNumber, comment)) {
+ lineIsComment = true;
+ } else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
+ line = stripTrailingComment(line, comment);
+ }
+ }
+ if (ignorePattern && ignorePattern.test(line) ||
+ ignoreUrls && URL_REGEXP.test(line) ||
+ ignoreStrings && stringsByLine[lineNumber] ||
+ ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] ||
+ ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]
+ ) {
+
+ // ignore this line
+ return;
+ }
+
+ const lineLength = computeLineLength(line, tabWidth);
+ const commentLengthApplies = lineIsComment && maxCommentLength;
+
+ if (lineIsComment && ignoreComments) {
+ return;
+ }
+
+ if (commentLengthApplies) {
+ if (lineLength > maxCommentLength) {
+ context.report({
+ node,
+ loc: { line: lineNumber, column: 0 },
+ message: "Line {{lineNumber}} exceeds the maximum comment line length of {{maxCommentLength}}.",
+ data: {
+ lineNumber: i + 1,
+ maxCommentLength
+ }
+ });
+ }
+ } else if (lineLength > maxLength) {
+ context.report({
+ node,
+ loc: { line: lineNumber, column: 0 },
+ message: "Line {{lineNumber}} exceeds the maximum line length of {{maxLength}}.",
+ data: {
+ lineNumber: i + 1,
+ maxLength
+ }
+ });
+ }
+ });
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: checkProgramForMaxLength
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-lines.js b/tools/node_modules/eslint/lib/rules/max-lines.js
new file mode 100644
index 0000000000..297c75dc13
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-lines.js
@@ -0,0 +1,144 @@
+/**
+ * @fileoverview enforce a maximum file length
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum number of lines per file",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ max: {
+ type: "integer",
+ minimum: 0
+ },
+ skipComments: {
+ type: "boolean"
+ },
+ skipBlankLines: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const option = context.options[0];
+ let max = 300;
+
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
+ max = option.max;
+ }
+
+ if (typeof option === "number") {
+ max = option;
+ }
+
+ const skipComments = option && option.skipComments;
+ const skipBlankLines = option && option.skipBlankLines;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Returns whether or not a token is a comment node type
+ * @param {Token} token The token to check
+ * @returns {boolean} True if the token is a comment node
+ */
+ function isCommentNodeType(token) {
+ return token && (token.type === "Block" || token.type === "Line");
+ }
+
+ /**
+ * Returns the line numbers of a comment that don't have any code on the same line
+ * @param {Node} comment The comment node to check
+ * @returns {int[]} The line numbers
+ */
+ function getLinesWithoutCode(comment) {
+ let start = comment.loc.start.line;
+ let end = comment.loc.end.line;
+
+ let token;
+
+ token = comment;
+ do {
+ token = sourceCode.getTokenBefore(token, { includeComments: true });
+ } while (isCommentNodeType(token));
+
+ if (token && astUtils.isTokenOnSameLine(token, comment)) {
+ start += 1;
+ }
+
+ token = comment;
+ do {
+ token = sourceCode.getTokenAfter(token, { includeComments: true });
+ } while (isCommentNodeType(token));
+
+ if (token && astUtils.isTokenOnSameLine(comment, token)) {
+ end -= 1;
+ }
+
+ if (start <= end) {
+ return lodash.range(start, end + 1);
+ }
+ return [];
+ }
+
+ return {
+ "Program:exit"() {
+ let lines = sourceCode.lines.map((text, i) => ({ lineNumber: i + 1, text }));
+
+ if (skipBlankLines) {
+ lines = lines.filter(l => l.text.trim() !== "");
+ }
+
+ if (skipComments) {
+ const comments = sourceCode.getAllComments();
+
+ const commentLines = lodash.flatten(comments.map(comment => getLinesWithoutCode(comment)));
+
+ lines = lines.filter(l => !lodash.includes(commentLines, l.lineNumber));
+ }
+
+ if (lines.length > max) {
+ context.report({
+ loc: { line: 1, column: 0 },
+ message: "File must be at most {{max}} lines long. It's {{actual}} lines long.",
+ data: {
+ max,
+ actual: lines.length
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-nested-callbacks.js b/tools/node_modules/eslint/lib/rules/max-nested-callbacks.js
new file mode 100644
index 0000000000..a89f49ae02
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-nested-callbacks.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Rule to enforce a maximum number of nested callbacks.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum depth that callbacks can be nested",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ maximum: {
+ type: "integer",
+ minimum: 0
+ },
+ max: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Constants
+ //--------------------------------------------------------------------------
+ const option = context.options[0];
+ let THRESHOLD = 10;
+
+ if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
+ THRESHOLD = option.maximum;
+ }
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
+ THRESHOLD = option.max;
+ }
+ if (typeof option === "number") {
+ THRESHOLD = option;
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const callbackStack = [];
+
+ /**
+ * Checks a given function node for too many callbacks.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkFunction(node) {
+ const parent = node.parent;
+
+ if (parent.type === "CallExpression") {
+ callbackStack.push(node);
+ }
+
+ if (callbackStack.length > THRESHOLD) {
+ const opts = { num: callbackStack.length, max: THRESHOLD };
+
+ context.report({ node, message: "Too many nested callbacks ({{num}}). Maximum allowed is {{max}}.", data: opts });
+ }
+ }
+
+ /**
+ * Pops the call stack.
+ * @returns {void}
+ * @private
+ */
+ function popStack() {
+ callbackStack.pop();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ ArrowFunctionExpression: checkFunction,
+ "ArrowFunctionExpression:exit": popStack,
+
+ FunctionExpression: checkFunction,
+ "FunctionExpression:exit": popStack
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-params.js b/tools/node_modules/eslint/lib/rules/max-params.js
new file mode 100644
index 0000000000..85838adacc
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-params.js
@@ -0,0 +1,96 @@
+/**
+ * @fileoverview Rule to flag when a function has too many parameters
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum number of parameters in function definitions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ maximum: {
+ type: "integer",
+ minimum: 0
+ },
+ max: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const option = context.options[0];
+ let numParams = 3;
+
+ if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
+ numParams = option.maximum;
+ }
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
+ numParams = option.max;
+ }
+ if (typeof option === "number") {
+ numParams = option;
+ }
+
+ /**
+ * Checks a function to see if it has too many parameters.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkFunction(node) {
+ if (node.params.length > numParams) {
+ context.report({
+ node,
+ message: "{{name}} has too many parameters ({{count}}). Maximum allowed is {{max}}.",
+ data: {
+ name: lodash.upperFirst(astUtils.getFunctionNameWithKind(node)),
+ count: node.params.length,
+ max: numParams
+ }
+ });
+ }
+ }
+
+ return {
+ FunctionDeclaration: checkFunction,
+ ArrowFunctionExpression: checkFunction,
+ FunctionExpression: checkFunction
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-statements-per-line.js b/tools/node_modules/eslint/lib/rules/max-statements-per-line.js
new file mode 100644
index 0000000000..3d18da4ee1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-statements-per-line.js
@@ -0,0 +1,194 @@
+/**
+ * @fileoverview Specify the maximum number of statements allowed per line.
+ * @author Kenneth Williams
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum number of statements allowed per line",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ max: {
+ type: "integer",
+ minimum: 1
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode(),
+ options = context.options[0] || {},
+ maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1,
+ message = "This line has {{numberOfStatementsOnThisLine}} {{statements}}. Maximum allowed is {{maxStatementsPerLine}}.";
+
+ let lastStatementLine = 0,
+ numberOfStatementsOnThisLine = 0,
+ firstExtraStatement;
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/;
+
+ /**
+ * Reports with the first extra statement, and clears it.
+ *
+ * @returns {void}
+ */
+ function reportFirstExtraStatementAndClear() {
+ if (firstExtraStatement) {
+ context.report({
+ node: firstExtraStatement,
+ message,
+ data: {
+ numberOfStatementsOnThisLine,
+ maxStatementsPerLine,
+ statements: numberOfStatementsOnThisLine === 1 ? "statement" : "statements"
+ }
+ });
+ }
+ firstExtraStatement = null;
+ }
+
+ /**
+ * Gets the actual last token of a given node.
+ *
+ * @param {ASTNode} node - A node to get. This is a node except EmptyStatement.
+ * @returns {Token} The actual last token.
+ */
+ function getActualLastToken(node) {
+ return sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
+ }
+
+ /**
+ * Addresses a given node.
+ * It updates the state of this rule, then reports the node if the node violated this rule.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {void}
+ */
+ function enterStatement(node) {
+ const line = node.loc.start.line;
+
+ /*
+ * Skip to allow non-block statements if this is direct child of control statements.
+ * `if (a) foo();` is counted as 1.
+ * But `if (a) foo(); else foo();` should be counted as 2.
+ */
+ if (SINGLE_CHILD_ALLOWED.test(node.parent.type) &&
+ node.parent.alternate !== node
+ ) {
+ return;
+ }
+
+ // Update state.
+ if (line === lastStatementLine) {
+ numberOfStatementsOnThisLine += 1;
+ } else {
+ reportFirstExtraStatementAndClear();
+ numberOfStatementsOnThisLine = 1;
+ lastStatementLine = line;
+ }
+
+ // Reports if the node violated this rule.
+ if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) {
+ firstExtraStatement = firstExtraStatement || node;
+ }
+ }
+
+ /**
+ * Updates the state of this rule with the end line of leaving node to check with the next statement.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {void}
+ */
+ function leaveStatement(node) {
+ const line = getActualLastToken(node).loc.end.line;
+
+ // Update state.
+ if (line !== lastStatementLine) {
+ reportFirstExtraStatementAndClear();
+ numberOfStatementsOnThisLine = 1;
+ lastStatementLine = line;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ BreakStatement: enterStatement,
+ ClassDeclaration: enterStatement,
+ ContinueStatement: enterStatement,
+ DebuggerStatement: enterStatement,
+ DoWhileStatement: enterStatement,
+ ExpressionStatement: enterStatement,
+ ForInStatement: enterStatement,
+ ForOfStatement: enterStatement,
+ ForStatement: enterStatement,
+ FunctionDeclaration: enterStatement,
+ IfStatement: enterStatement,
+ ImportDeclaration: enterStatement,
+ LabeledStatement: enterStatement,
+ ReturnStatement: enterStatement,
+ SwitchStatement: enterStatement,
+ ThrowStatement: enterStatement,
+ TryStatement: enterStatement,
+ VariableDeclaration: enterStatement,
+ WhileStatement: enterStatement,
+ WithStatement: enterStatement,
+ ExportNamedDeclaration: enterStatement,
+ ExportDefaultDeclaration: enterStatement,
+ ExportAllDeclaration: enterStatement,
+
+ "BreakStatement:exit": leaveStatement,
+ "ClassDeclaration:exit": leaveStatement,
+ "ContinueStatement:exit": leaveStatement,
+ "DebuggerStatement:exit": leaveStatement,
+ "DoWhileStatement:exit": leaveStatement,
+ "ExpressionStatement:exit": leaveStatement,
+ "ForInStatement:exit": leaveStatement,
+ "ForOfStatement:exit": leaveStatement,
+ "ForStatement:exit": leaveStatement,
+ "FunctionDeclaration:exit": leaveStatement,
+ "IfStatement:exit": leaveStatement,
+ "ImportDeclaration:exit": leaveStatement,
+ "LabeledStatement:exit": leaveStatement,
+ "ReturnStatement:exit": leaveStatement,
+ "SwitchStatement:exit": leaveStatement,
+ "ThrowStatement:exit": leaveStatement,
+ "TryStatement:exit": leaveStatement,
+ "VariableDeclaration:exit": leaveStatement,
+ "WhileStatement:exit": leaveStatement,
+ "WithStatement:exit": leaveStatement,
+ "ExportNamedDeclaration:exit": leaveStatement,
+ "ExportDefaultDeclaration:exit": leaveStatement,
+ "ExportAllDeclaration:exit": leaveStatement,
+ "Program:exit": reportFirstExtraStatementAndClear
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/max-statements.js b/tools/node_modules/eslint/lib/rules/max-statements.js
new file mode 100644
index 0000000000..f98aa3a21d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/max-statements.js
@@ -0,0 +1,170 @@
+/**
+ * @fileoverview A rule to set the maximum number of statements in a function.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a maximum number of statements allowed in function blocks",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ type: "object",
+ properties: {
+ maximum: {
+ type: "integer",
+ minimum: 0
+ },
+ max: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ {
+ type: "object",
+ properties: {
+ ignoreTopLevelFunctions: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const functionStack = [],
+ option = context.options[0],
+ ignoreTopLevelFunctions = context.options[1] && context.options[1].ignoreTopLevelFunctions || false,
+ topLevelFunctions = [];
+ let maxStatements = 10;
+
+ if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
+ maxStatements = option.maximum;
+ }
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
+ maxStatements = option.max;
+ }
+ if (typeof option === "number") {
+ maxStatements = option;
+ }
+
+ /**
+ * Reports a node if it has too many statements
+ * @param {ASTNode} node node to evaluate
+ * @param {int} count Number of statements in node
+ * @param {int} max Maximum number of statements allowed
+ * @returns {void}
+ * @private
+ */
+ function reportIfTooManyStatements(node, count, max) {
+ if (count > max) {
+ const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
+
+ context.report({
+ node,
+ message: "{{name}} has too many statements ({{count}}). Maximum allowed is {{max}}.",
+ data: { name, count, max }
+ });
+ }
+ }
+
+ /**
+ * When parsing a new function, store it in our function stack
+ * @returns {void}
+ * @private
+ */
+ function startFunction() {
+ functionStack.push(0);
+ }
+
+ /**
+ * Evaluate the node at the end of function
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function endFunction(node) {
+ const count = functionStack.pop();
+
+ if (ignoreTopLevelFunctions && functionStack.length === 0) {
+ topLevelFunctions.push({ node, count });
+ } else {
+ reportIfTooManyStatements(node, count, maxStatements);
+ }
+ }
+
+ /**
+ * Increment the count of the functions
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function countStatements(node) {
+ functionStack[functionStack.length - 1] += node.body.length;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ FunctionDeclaration: startFunction,
+ FunctionExpression: startFunction,
+ ArrowFunctionExpression: startFunction,
+
+ BlockStatement: countStatements,
+
+ "FunctionDeclaration:exit": endFunction,
+ "FunctionExpression:exit": endFunction,
+ "ArrowFunctionExpression:exit": endFunction,
+
+ "Program:exit"() {
+ if (topLevelFunctions.length === 1) {
+ return;
+ }
+
+ topLevelFunctions.forEach(element => {
+ const count = element.count;
+ const node = element.node;
+
+ reportIfTooManyStatements(node, count, maxStatements);
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js
new file mode 100644
index 0000000000..db4f768443
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js
@@ -0,0 +1,294 @@
+/**
+ * @fileoverview enforce a particular style for multiline comments
+ * @author Teddy Katz
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce a particular style for multiline comments",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [{ enum: ["starred-block", "separate-lines", "bare-block"] }]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const option = context.options[0] || "starred-block";
+
+ const EXPECTED_BLOCK_ERROR = "Expected a block comment instead of consecutive line comments.";
+ const START_NEWLINE_ERROR = "Expected a linebreak after '/*'.";
+ const END_NEWLINE_ERROR = "Expected a linebreak before '*/'.";
+ const MISSING_STAR_ERROR = "Expected a '*' at the start of this line.";
+ const ALIGNMENT_ERROR = "Expected this line to be aligned with the start of the comment.";
+ const EXPECTED_LINES_ERROR = "Expected multiple line comments instead of a block comment.";
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Gets a list of comment lines in a group
+ * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
+ * @returns {string[]} A list of comment lines
+ */
+ function getCommentLines(commentGroup) {
+ if (commentGroup[0].type === "Line") {
+ return commentGroup.map(comment => comment.value);
+ }
+ return commentGroup[0].value
+ .split(astUtils.LINEBREAK_MATCHER)
+ .map(line => line.replace(/^\s*\*?/, ""));
+ }
+
+ /**
+ * Converts a comment into starred-block form
+ * @param {Token} firstComment The first comment of the group being converted
+ * @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
+ * @returns {string} A representation of the comment value in starred-block form, excluding start and end markers
+ */
+ function convertToStarredBlock(firstComment, commentLinesList) {
+ const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
+ const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`);
+
+ return `\n${starredLines.join("\n")}\n${initialOffset} `;
+ }
+
+ /**
+ * Converts a comment into separate-line form
+ * @param {Token} firstComment The first comment of the group being converted
+ * @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
+ * @returns {string} A representation of the comment value in separate-line form
+ */
+ function convertToSeparateLines(firstComment, commentLinesList) {
+ const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
+ const separateLines = commentLinesList.map(line => `// ${line.trim()}`);
+
+ return separateLines.join(`\n${initialOffset}`);
+ }
+
+ /**
+ * Converts a comment into bare-block form
+ * @param {Token} firstComment The first comment of the group being converted
+ * @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
+ * @returns {string} A representation of the comment value in bare-block form
+ */
+ function convertToBlock(firstComment, commentLinesList) {
+ const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
+ const blockLines = commentLinesList.map(line => line.trim());
+
+ return `/* ${blockLines.join(`\n${initialOffset} `)} */`;
+ }
+
+ /**
+ * Check a comment is JSDoc form
+ * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
+ * @returns {boolean} if commentGroup is JSDoc form, return true
+ */
+ function isJSDoc(commentGroup) {
+ const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER);
+
+ return commentGroup[0].type === "Block" &&
+ /^\*\s*$/.test(lines[0]) &&
+ lines.slice(1, -1).every(line => /^\s* /.test(line)) &&
+ /^\s*$/.test(lines[lines.length - 1]);
+ }
+
+ /**
+ * Each method checks a group of comments to see if it's valid according to the given option.
+ * @param {Token[]} commentGroup A list of comments that appear together. This will either contain a single
+ * block comment or multiple line comments.
+ * @returns {void}
+ */
+ const commentGroupCheckers = {
+ "starred-block"(commentGroup) {
+ const commentLines = getCommentLines(commentGroup);
+
+ if (commentLines.some(value => value.includes("*/"))) {
+ return;
+ }
+
+ if (commentGroup.length > 1) {
+ context.report({
+ loc: {
+ start: commentGroup[0].loc.start,
+ end: commentGroup[commentGroup.length - 1].loc.end
+ },
+ message: EXPECTED_BLOCK_ERROR,
+ fix(fixer) {
+ const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
+ const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
+
+ return commentLines.some(value => value.startsWith("/"))
+ ? null
+ : fixer.replaceTextRange(range, starredBlock);
+ }
+ });
+ } else {
+ const block = commentGroup[0];
+ const lines = block.value.split(astUtils.LINEBREAK_MATCHER);
+ const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`;
+
+ if (!/^\*?\s*$/.test(lines[0])) {
+ const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0];
+
+ context.report({
+ loc: {
+ start: block.loc.start,
+ end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
+ },
+ message: START_NEWLINE_ERROR,
+ fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`)
+ });
+ }
+
+ if (!/^\s*$/.test(lines[lines.length - 1])) {
+ context.report({
+ loc: {
+ start: { line: block.loc.end.line, column: block.loc.end.column - 2 },
+ end: block.loc.end
+ },
+ message: END_NEWLINE_ERROR,
+ fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`)
+ });
+ }
+
+ for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) {
+ const lineText = sourceCode.lines[lineNumber - 1];
+
+ if (!lineText.startsWith(expectedLinePrefix)) {
+ context.report({
+ loc: {
+ start: { line: lineNumber, column: 0 },
+ end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length }
+ },
+ message: /^\s*\*/.test(lineText)
+ ? ALIGNMENT_ERROR
+ : MISSING_STAR_ERROR,
+ fix(fixer) {
+ const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 });
+ const linePrefixLength = lineText.match(/^\s*\*? ?/)[0].length;
+ const commentStartIndex = lineStartIndex + linePrefixLength;
+
+ const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength
+ ? expectedLinePrefix
+ : `${expectedLinePrefix} `;
+
+ return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText);
+ }
+ });
+ }
+ }
+ }
+ },
+ "separate-lines"(commentGroup) {
+ if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") {
+ const commentLines = getCommentLines(commentGroup);
+ const block = commentGroup[0];
+ const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true });
+
+ if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) {
+ return;
+ }
+
+ context.report({
+ loc: {
+ start: block.loc.start,
+ end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
+ },
+ message: EXPECTED_LINES_ERROR,
+ fix(fixer) {
+ return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line)));
+ }
+ });
+ }
+ },
+ "bare-block"(commentGroup) {
+ if (!isJSDoc(commentGroup)) {
+ const commentLines = getCommentLines(commentGroup);
+
+ // disallows consecutive line comments in favor of using a block comment.
+ if (commentGroup[0].type === "Line" && commentLines.length > 1 &&
+ !commentLines.some(value => value.includes("*/"))) {
+ context.report({
+ loc: {
+ start: commentGroup[0].loc.start,
+ end: commentGroup[commentGroup.length - 1].loc.end
+ },
+ message: EXPECTED_BLOCK_ERROR,
+ fix(fixer) {
+ const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
+ const block = convertToBlock(commentGroup[0], commentLines.filter(line => line));
+
+ return fixer.replaceTextRange(range, block);
+ }
+ });
+ }
+
+ // prohibits block comments from having a * at the beginning of each line.
+ if (commentGroup[0].type === "Block") {
+ const block = commentGroup[0];
+ const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim());
+
+ if (lines.length > 0 && lines.every(line => /^\s*\*/.test(line))) {
+ context.report({
+ loc: {
+ start: block.loc.start,
+ end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
+ },
+ message: EXPECTED_BLOCK_ERROR,
+ fix(fixer) {
+ return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line)));
+ }
+ });
+ }
+ }
+ }
+ }
+ };
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ Program() {
+ return sourceCode.getAllComments()
+ .filter(comment => comment.type !== "Shebang")
+ .filter(comment => !astUtils.COMMENTS_IGNORE_PATTERN.test(comment.value))
+ .filter(comment => {
+ const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
+
+ return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
+ })
+ .reduce((commentGroups, comment, index, commentList) => {
+ const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
+
+ if (
+ comment.type === "Line" &&
+ index && commentList[index - 1].type === "Line" &&
+ tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
+ tokenBefore === commentList[index - 1]
+ ) {
+ commentGroups[commentGroups.length - 1].push(comment);
+ } else {
+ commentGroups.push([comment]);
+ }
+
+ return commentGroups;
+ }, [])
+ .filter(commentGroup => !(commentGroup.length === 1 && commentGroup[0].loc.start.line === commentGroup[0].loc.end.line))
+ .forEach(commentGroupCheckers[option]);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/multiline-ternary.js b/tools/node_modules/eslint/lib/rules/multiline-ternary.js
new file mode 100644
index 0000000000..a74f241d86
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/multiline-ternary.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview Enforce newlines between operands of ternary expressions
+ * @author Kai Cataldo
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce newlines between operands of ternary expressions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: [
+ {
+ enum: ["always", "always-multiline", "never"]
+ }
+ ]
+ },
+
+ create(context) {
+ const option = context.options[0];
+ const multiline = option !== "never";
+ const allowSingleLine = option === "always-multiline";
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Tests whether node is preceded by supplied tokens
+ * @param {ASTNode} node - node to check
+ * @param {ASTNode} parentNode - parent of node to report
+ * @param {boolean} expected - whether newline was expected or not
+ * @returns {void}
+ * @private
+ */
+ function reportError(node, parentNode, expected) {
+ context.report({
+ node,
+ message: "{{expected}} newline between {{typeOfError}} of ternary expression.",
+ data: {
+ expected: expected ? "Expected" : "Unexpected",
+ typeOfError: node === parentNode.test ? "test and consequent" : "consequent and alternate"
+ }
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ ConditionalExpression(node) {
+ const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(node.test, node.consequent);
+ const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(node.consequent, node.alternate);
+
+ if (!multiline) {
+ if (!areTestAndConsequentOnSameLine) {
+ reportError(node.test, node, false);
+ }
+
+ if (!areConsequentAndAlternateOnSameLine) {
+ reportError(node.consequent, node, false);
+ }
+ } else {
+ if (allowSingleLine && node.loc.start.line === node.loc.end.line) {
+ return;
+ }
+
+ if (areTestAndConsequentOnSameLine) {
+ reportError(node.test, node, true);
+ }
+
+ if (areConsequentAndAlternateOnSameLine) {
+ reportError(node.consequent, node, true);
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/new-cap.js b/tools/node_modules/eslint/lib/rules/new-cap.js
new file mode 100644
index 0000000000..f01e8f90ac
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/new-cap.js
@@ -0,0 +1,272 @@
+/**
+ * @fileoverview Rule to flag use of constructors without capital letters
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const CAPS_ALLOWED = [
+ "Array",
+ "Boolean",
+ "Date",
+ "Error",
+ "Function",
+ "Number",
+ "Object",
+ "RegExp",
+ "String",
+ "Symbol"
+];
+
+/**
+ * Ensure that if the key is provided, it must be an array.
+ * @param {Object} obj Object to check with `key`.
+ * @param {string} key Object key to check on `obj`.
+ * @param {*} fallback If obj[key] is not present, this will be returned.
+ * @returns {string[]} Returns obj[key] if it's an Array, otherwise `fallback`
+ */
+function checkArray(obj, key, fallback) {
+
+ /* istanbul ignore if */
+ if (Object.prototype.hasOwnProperty.call(obj, key) && !Array.isArray(obj[key])) {
+ throw new TypeError(`${key}, if provided, must be an Array`);
+ }
+ return obj[key] || fallback;
+}
+
+/**
+ * A reducer function to invert an array to an Object mapping the string form of the key, to `true`.
+ * @param {Object} map Accumulator object for the reduce.
+ * @param {string} key Object key to set to `true`.
+ * @returns {Object} Returns the updated Object for further reduction.
+ */
+function invert(map, key) {
+ map[key] = true;
+ return map;
+}
+
+/**
+ * Creates an object with the cap is new exceptions as its keys and true as their values.
+ * @param {Object} config Rule configuration
+ * @returns {Object} Object with cap is new exceptions.
+ */
+function calculateCapIsNewExceptions(config) {
+ let capIsNewExceptions = checkArray(config, "capIsNewExceptions", CAPS_ALLOWED);
+
+ if (capIsNewExceptions !== CAPS_ALLOWED) {
+ capIsNewExceptions = capIsNewExceptions.concat(CAPS_ALLOWED);
+ }
+
+ return capIsNewExceptions.reduce(invert, {});
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require constructor names to begin with a capital letter",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ newIsCap: {
+ type: "boolean"
+ },
+ capIsNew: {
+ type: "boolean"
+ },
+ newIsCapExceptions: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ newIsCapExceptionPattern: {
+ type: "string"
+ },
+ capIsNewExceptions: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ capIsNewExceptionPattern: {
+ type: "string"
+ },
+ properties: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const config = context.options[0] ? Object.assign({}, context.options[0]) : {};
+
+ config.newIsCap = config.newIsCap !== false;
+ config.capIsNew = config.capIsNew !== false;
+ const skipProperties = config.properties === false;
+
+ const newIsCapExceptions = checkArray(config, "newIsCapExceptions", []).reduce(invert, {});
+ const newIsCapExceptionPattern = config.newIsCapExceptionPattern ? new RegExp(config.newIsCapExceptionPattern) : null;
+
+ const capIsNewExceptions = calculateCapIsNewExceptions(config);
+ const capIsNewExceptionPattern = config.capIsNewExceptionPattern ? new RegExp(config.capIsNewExceptionPattern) : null;
+
+ const listeners = {};
+
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Get exact callee name from expression
+ * @param {ASTNode} node CallExpression or NewExpression node
+ * @returns {string} name
+ */
+ function extractNameFromExpression(node) {
+
+ let name = "";
+
+ if (node.callee.type === "MemberExpression") {
+ const property = node.callee.property;
+
+ if (property.type === "Literal" && (typeof property.value === "string")) {
+ name = property.value;
+ } else if (property.type === "Identifier" && !node.callee.computed) {
+ name = property.name;
+ }
+ } else {
+ name = node.callee.name;
+ }
+ return name;
+ }
+
+ /**
+ * Returns the capitalization state of the string -
+ * Whether the first character is uppercase, lowercase, or non-alphabetic
+ * @param {string} str String
+ * @returns {string} capitalization state: "non-alpha", "lower", or "upper"
+ */
+ function getCap(str) {
+ const firstChar = str.charAt(0);
+
+ const firstCharLower = firstChar.toLowerCase();
+ const firstCharUpper = firstChar.toUpperCase();
+
+ if (firstCharLower === firstCharUpper) {
+
+ // char has no uppercase variant, so it's non-alphabetic
+ return "non-alpha";
+ }
+ if (firstChar === firstCharLower) {
+ return "lower";
+ }
+ return "upper";
+
+ }
+
+ /**
+ * Check if capitalization is allowed for a CallExpression
+ * @param {Object} allowedMap Object mapping calleeName to a Boolean
+ * @param {ASTNode} node CallExpression node
+ * @param {string} calleeName Capitalized callee name from a CallExpression
+ * @param {Object} pattern RegExp object from options pattern
+ * @returns {boolean} Returns true if the callee may be capitalized
+ */
+ function isCapAllowed(allowedMap, node, calleeName, pattern) {
+ const sourceText = sourceCode.getText(node.callee);
+
+ if (allowedMap[calleeName] || allowedMap[sourceText]) {
+ return true;
+ }
+
+ if (pattern && pattern.test(sourceText)) {
+ return true;
+ }
+
+ if (calleeName === "UTC" && node.callee.type === "MemberExpression") {
+
+ // allow if callee is Date.UTC
+ return node.callee.object.type === "Identifier" &&
+ node.callee.object.name === "Date";
+ }
+
+ return skipProperties && node.callee.type === "MemberExpression";
+ }
+
+ /**
+ * Reports the given message for the given node. The location will be the start of the property or the callee.
+ * @param {ASTNode} node CallExpression or NewExpression node.
+ * @param {string} message The message to report.
+ * @returns {void}
+ */
+ function report(node, message) {
+ let callee = node.callee;
+
+ if (callee.type === "MemberExpression") {
+ callee = callee.property;
+ }
+
+ context.report({ node, loc: callee.loc.start, message });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ if (config.newIsCap) {
+ listeners.NewExpression = function(node) {
+
+ const constructorName = extractNameFromExpression(node);
+
+ if (constructorName) {
+ const capitalization = getCap(constructorName);
+ const isAllowed = capitalization !== "lower" || isCapAllowed(newIsCapExceptions, node, constructorName, newIsCapExceptionPattern);
+
+ if (!isAllowed) {
+ report(node, "A constructor name should not start with a lowercase letter.");
+ }
+ }
+ };
+ }
+
+ if (config.capIsNew) {
+ listeners.CallExpression = function(node) {
+
+ const calleeName = extractNameFromExpression(node);
+
+ if (calleeName) {
+ const capitalization = getCap(calleeName);
+ const isAllowed = capitalization !== "upper" || isCapAllowed(capIsNewExceptions, node, calleeName, capIsNewExceptionPattern);
+
+ if (!isAllowed) {
+ report(node, "A function with a name starting with an uppercase letter should only be used as a constructor.");
+ }
+ }
+ };
+ }
+
+ return listeners;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/new-parens.js b/tools/node_modules/eslint/lib/rules/new-parens.js
new file mode 100644
index 0000000000..aa0f7fe931
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/new-parens.js
@@ -0,0 +1,58 @@
+/**
+ * @fileoverview Rule to flag when using constructor without parentheses
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require parentheses when invoking a constructor with no arguments",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ NewExpression(node) {
+ if (node.arguments.length !== 0) {
+ return; // shortcut: if there are arguments, there have to be parens
+ }
+
+ const lastToken = sourceCode.getLastToken(node);
+ const hasLastParen = lastToken && astUtils.isClosingParenToken(lastToken);
+ const hasParens = hasLastParen && astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken));
+
+ if (!hasParens) {
+ context.report({
+ node,
+ message: "Missing '()' invoking a constructor.",
+ fix: fixer => fixer.insertTextAfter(node, "()")
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/newline-after-var.js b/tools/node_modules/eslint/lib/rules/newline-after-var.js
new file mode 100644
index 0000000000..80f73c836f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/newline-after-var.js
@@ -0,0 +1,254 @@
+/**
+ * @fileoverview Rule to check empty newline after "var" statement
+ * @author Gopal Venkatesan
+ * @deprecated
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow an empty line after variable declarations",
+ category: "Stylistic Issues",
+ recommended: false,
+ replacedBy: ["padding-line-between-statements"]
+ },
+
+ schema: [
+ {
+ enum: ["never", "always"]
+ }
+ ],
+
+ fixable: "whitespace",
+
+ deprecated: true
+ },
+
+ create(context) {
+
+ const ALWAYS_MESSAGE = "Expected blank line after variable declarations.",
+ NEVER_MESSAGE = "Unexpected blank line after variable declarations.";
+
+ const sourceCode = context.getSourceCode();
+
+ // Default `mode` to "always".
+ const mode = context.options[0] === "never" ? "never" : "always";
+
+ // Cache starting and ending line numbers of comments for faster lookup
+ const commentEndLine = sourceCode.getAllComments().reduce((result, token) => {
+ result[token.loc.start.line] = token.loc.end.line;
+ return result;
+ }, {});
+
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Gets a token from the given node to compare line to the next statement.
+ *
+ * In general, the token is the last token of the node. However, the token is the second last token if the following conditions satisfy.
+ *
+ * - The last token is semicolon.
+ * - The semicolon is on a different line from the previous token of the semicolon.
+ *
+ * This behavior would address semicolon-less style code. e.g.:
+ *
+ * var foo = 1
+ *
+ * ;(a || b).doSomething()
+ *
+ * @param {ASTNode} node - The node to get.
+ * @returns {Token} The token to compare line to the next statement.
+ */
+ function getLastToken(node) {
+ const lastToken = sourceCode.getLastToken(node);
+
+ if (lastToken.type === "Punctuator" && lastToken.value === ";") {
+ const prevToken = sourceCode.getTokenBefore(lastToken);
+
+ if (prevToken.loc.end.line !== lastToken.loc.start.line) {
+ return prevToken;
+ }
+ }
+
+ return lastToken;
+ }
+
+ /**
+ * Determine if provided keyword is a variable declaration
+ * @private
+ * @param {string} keyword - keyword to test
+ * @returns {boolean} True if `keyword` is a type of var
+ */
+ function isVar(keyword) {
+ return keyword === "var" || keyword === "let" || keyword === "const";
+ }
+
+ /**
+ * Determine if provided keyword is a variant of for specifiers
+ * @private
+ * @param {string} keyword - keyword to test
+ * @returns {boolean} True if `keyword` is a variant of for specifier
+ */
+ function isForTypeSpecifier(keyword) {
+ return keyword === "ForStatement" || keyword === "ForInStatement" || keyword === "ForOfStatement";
+ }
+
+ /**
+ * Determine if provided keyword is an export specifiers
+ * @private
+ * @param {string} nodeType - nodeType to test
+ * @returns {boolean} True if `nodeType` is an export specifier
+ */
+ function isExportSpecifier(nodeType) {
+ return nodeType === "ExportNamedDeclaration" || nodeType === "ExportSpecifier" ||
+ nodeType === "ExportDefaultDeclaration" || nodeType === "ExportAllDeclaration";
+ }
+
+ /**
+ * Determine if provided node is the last of their parent block.
+ * @private
+ * @param {ASTNode} node - node to test
+ * @returns {boolean} True if `node` is last of their parent block.
+ */
+ function isLastNode(node) {
+ const token = sourceCode.getTokenAfter(node);
+
+ return !token || (token.type === "Punctuator" && token.value === "}");
+ }
+
+ /**
+ * Gets the last line of a group of consecutive comments
+ * @param {number} commentStartLine The starting line of the group
+ * @returns {number} The number of the last comment line of the group
+ */
+ function getLastCommentLineOfBlock(commentStartLine) {
+ const currentCommentEnd = commentEndLine[commentStartLine];
+
+ return commentEndLine[currentCommentEnd + 1] ? getLastCommentLineOfBlock(currentCommentEnd + 1) : currentCommentEnd;
+ }
+
+ /**
+ * Determine if a token starts more than one line after a comment ends
+ * @param {token} token The token being checked
+ * @param {integer} commentStartLine The line number on which the comment starts
+ * @returns {boolean} True if `token` does not start immediately after a comment
+ */
+ function hasBlankLineAfterComment(token, commentStartLine) {
+ return token.loc.start.line > getLastCommentLineOfBlock(commentStartLine) + 1;
+ }
+
+ /**
+ * Checks that a blank line exists after a variable declaration when mode is
+ * set to "always", or checks that there is no blank line when mode is set
+ * to "never"
+ * @private
+ * @param {ASTNode} node - `VariableDeclaration` node to test
+ * @returns {void}
+ */
+ function checkForBlankLine(node) {
+
+ /*
+ * lastToken is the last token on the node's line. It will usually also be the last token of the node, but it will
+ * sometimes be second-last if there is a semicolon on a different line.
+ */
+ const lastToken = getLastToken(node),
+
+ /*
+ * If lastToken is the last token of the node, nextToken should be the token after the node. Otherwise, nextToken
+ * is the last token of the node.
+ */
+ nextToken = lastToken === sourceCode.getLastToken(node) ? sourceCode.getTokenAfter(node) : sourceCode.getLastToken(node),
+ nextLineNum = lastToken.loc.end.line + 1;
+
+ // Ignore if there is no following statement
+ if (!nextToken) {
+ return;
+ }
+
+ // Ignore if parent of node is a for variant
+ if (isForTypeSpecifier(node.parent.type)) {
+ return;
+ }
+
+ // Ignore if parent of node is an export specifier
+ if (isExportSpecifier(node.parent.type)) {
+ return;
+ }
+
+ /*
+ * Some coding styles use multiple `var` statements, so do nothing if
+ * the next token is a `var` statement.
+ */
+ if (nextToken.type === "Keyword" && isVar(nextToken.value)) {
+ return;
+ }
+
+ // Ignore if it is last statement in a block
+ if (isLastNode(node)) {
+ return;
+ }
+
+ // Next statement is not a `var`...
+ const noNextLineToken = nextToken.loc.start.line > nextLineNum;
+ const hasNextLineComment = (typeof commentEndLine[nextLineNum] !== "undefined");
+
+ if (mode === "never" && noNextLineToken && !hasNextLineComment) {
+ context.report({
+ node,
+ message: NEVER_MESSAGE,
+ data: { identifier: node.name },
+ fix(fixer) {
+ const linesBetween = sourceCode.getText().slice(lastToken.range[1], nextToken.range[0]).split(astUtils.LINEBREAK_MATCHER);
+
+ return fixer.replaceTextRange([lastToken.range[1], nextToken.range[0]], `${linesBetween.slice(0, -1).join("")}\n${linesBetween[linesBetween.length - 1]}`);
+ }
+ });
+ }
+
+ // Token on the next line, or comment without blank line
+ if (
+ mode === "always" && (
+ !noNextLineToken ||
+ hasNextLineComment && !hasBlankLineAfterComment(nextToken, nextLineNum)
+ )
+ ) {
+ context.report({
+ node,
+ message: ALWAYS_MESSAGE,
+ data: { identifier: node.name },
+ fix(fixer) {
+ if ((noNextLineToken ? getLastCommentLineOfBlock(nextLineNum) : lastToken.loc.end.line) === nextToken.loc.start.line) {
+ return fixer.insertTextBefore(nextToken, "\n\n");
+ }
+
+ return fixer.insertTextBeforeRange([nextToken.range[0] - nextToken.loc.start.column, nextToken.range[1]], "\n");
+ }
+ });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ VariableDeclaration: checkForBlankLine
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/newline-before-return.js b/tools/node_modules/eslint/lib/rules/newline-before-return.js
new file mode 100644
index 0000000000..02bd66db13
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/newline-before-return.js
@@ -0,0 +1,210 @@
+/**
+ * @fileoverview Rule to require newlines before `return` statement
+ * @author Kai Cataldo
+ * @deprecated
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require an empty line before `return` statements",
+ category: "Stylistic Issues",
+ recommended: false,
+ replacedBy: ["padding-line-between-statements"]
+ },
+ fixable: "whitespace",
+ schema: [],
+ deprecated: true
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Tests whether node is preceded by supplied tokens
+ * @param {ASTNode} node - node to check
+ * @param {array} testTokens - array of tokens to test against
+ * @returns {boolean} Whether or not the node is preceded by one of the supplied tokens
+ * @private
+ */
+ function isPrecededByTokens(node, testTokens) {
+ const tokenBefore = sourceCode.getTokenBefore(node);
+
+ return testTokens.some(token => tokenBefore.value === token);
+ }
+
+ /**
+ * Checks whether node is the first node after statement or in block
+ * @param {ASTNode} node - node to check
+ * @returns {boolean} Whether or not the node is the first node after statement or in block
+ * @private
+ */
+ function isFirstNode(node) {
+ const parentType = node.parent.type;
+
+ if (node.parent.body) {
+ return Array.isArray(node.parent.body)
+ ? node.parent.body[0] === node
+ : node.parent.body === node;
+ }
+
+ if (parentType === "IfStatement") {
+ return isPrecededByTokens(node, ["else", ")"]);
+ }
+ if (parentType === "DoWhileStatement") {
+ return isPrecededByTokens(node, ["do"]);
+ }
+ if (parentType === "SwitchCase") {
+ return isPrecededByTokens(node, [":"]);
+ }
+ return isPrecededByTokens(node, [")"]);
+
+ }
+
+ /**
+ * Returns the number of lines of comments that precede the node
+ * @param {ASTNode} node - node to check for overlapping comments
+ * @param {number} lineNumTokenBefore - line number of previous token, to check for overlapping comments
+ * @returns {number} Number of lines of comments that precede the node
+ * @private
+ */
+ function calcCommentLines(node, lineNumTokenBefore) {
+ const comments = sourceCode.getCommentsBefore(node);
+ let numLinesComments = 0;
+
+ if (!comments.length) {
+ return numLinesComments;
+ }
+
+ comments.forEach(comment => {
+ numLinesComments++;
+
+ if (comment.type === "Block") {
+ numLinesComments += comment.loc.end.line - comment.loc.start.line;
+ }
+
+ // avoid counting lines with inline comments twice
+ if (comment.loc.start.line === lineNumTokenBefore) {
+ numLinesComments--;
+ }
+
+ if (comment.loc.end.line === node.loc.start.line) {
+ numLinesComments--;
+ }
+ });
+
+ return numLinesComments;
+ }
+
+ /**
+ * Returns the line number of the token before the node that is passed in as an argument
+ * @param {ASTNode} node - The node to use as the start of the calculation
+ * @returns {number} Line number of the token before `node`
+ * @private
+ */
+ function getLineNumberOfTokenBefore(node) {
+ const tokenBefore = sourceCode.getTokenBefore(node);
+ let lineNumTokenBefore;
+
+ /**
+ * Global return (at the beginning of a script) is a special case.
+ * If there is no token before `return`, then we expect no line
+ * break before the return. Comments are allowed to occupy lines
+ * before the global return, just no blank lines.
+ * Setting lineNumTokenBefore to zero in that case results in the
+ * desired behavior.
+ */
+ if (tokenBefore) {
+ lineNumTokenBefore = tokenBefore.loc.end.line;
+ } else {
+ lineNumTokenBefore = 0; // global return at beginning of script
+ }
+
+ return lineNumTokenBefore;
+ }
+
+ /**
+ * Checks whether node is preceded by a newline
+ * @param {ASTNode} node - node to check
+ * @returns {boolean} Whether or not the node is preceded by a newline
+ * @private
+ */
+ function hasNewlineBefore(node) {
+ const lineNumNode = node.loc.start.line;
+ const lineNumTokenBefore = getLineNumberOfTokenBefore(node);
+ const commentLines = calcCommentLines(node, lineNumTokenBefore);
+
+ return (lineNumNode - lineNumTokenBefore - commentLines) > 1;
+ }
+
+ /**
+ * Checks whether it is safe to apply a fix to a given return statement.
+ *
+ * The fix is not considered safe if the given return statement has leading comments,
+ * as we cannot safely determine if the newline should be added before or after the comments.
+ * For more information, see: https://github.com/eslint/eslint/issues/5958#issuecomment-222767211
+ *
+ * @param {ASTNode} node - The return statement node to check.
+ * @returns {boolean} `true` if it can fix the node.
+ * @private
+ */
+ function canFix(node) {
+ const leadingComments = sourceCode.getCommentsBefore(node);
+ const lastLeadingComment = leadingComments[leadingComments.length - 1];
+ const tokenBefore = sourceCode.getTokenBefore(node);
+
+ if (leadingComments.length === 0) {
+ return true;
+ }
+
+ /*
+ * if the last leading comment ends in the same line as the previous token and
+ * does not share a line with the `return` node, we can consider it safe to fix.
+ * Example:
+ * function a() {
+ * var b; //comment
+ * return;
+ * }
+ */
+ if (lastLeadingComment.loc.end.line === tokenBefore.loc.end.line &&
+ lastLeadingComment.loc.end.line !== node.loc.start.line) {
+ return true;
+ }
+
+ return false;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ ReturnStatement(node) {
+ if (!isFirstNode(node) && !hasNewlineBefore(node)) {
+ context.report({
+ node,
+ message: "Expected newline before return statement.",
+ fix(fixer) {
+ if (canFix(node)) {
+ const tokenBefore = sourceCode.getTokenBefore(node);
+ const newlines = node.loc.start.line === tokenBefore.loc.end.line ? "\n\n" : "\n";
+
+ return fixer.insertTextBefore(node, newlines);
+ }
+ return null;
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/newline-per-chained-call.js b/tools/node_modules/eslint/lib/rules/newline-per-chained-call.js
new file mode 100644
index 0000000000..356c4baf32
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/newline-per-chained-call.js
@@ -0,0 +1,103 @@
+/**
+ * @fileoverview Rule to ensure newline per method call when chaining calls
+ * @author Rajendra Patil
+ * @author Burak Yigit Kaya
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require a newline after each call in a method chain",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [{
+ type: "object",
+ properties: {
+ ignoreChainWithDepth: {
+ type: "integer",
+ minimum: 1,
+ maximum: 10
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+
+ const options = context.options[0] || {},
+ ignoreChainWithDepth = options.ignoreChainWithDepth || 2;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Get the prefix of a given MemberExpression node.
+ * If the MemberExpression node is a computed value it returns a
+ * left bracket. If not it returns a period.
+ *
+ * @param {ASTNode} node - A MemberExpression node to get
+ * @returns {string} The prefix of the node.
+ */
+ function getPrefix(node) {
+ return node.computed ? "[" : ".";
+ }
+
+ /**
+ * Gets the property text of a given MemberExpression node.
+ * If the text is multiline, this returns only the first line.
+ *
+ * @param {ASTNode} node - A MemberExpression node to get.
+ * @returns {string} The property text of the node.
+ */
+ function getPropertyText(node) {
+ const prefix = getPrefix(node);
+ const lines = sourceCode.getText(node.property).split(astUtils.LINEBREAK_MATCHER);
+ const suffix = node.computed && lines.length === 1 ? "]" : "";
+
+ return prefix + lines[0] + suffix;
+ }
+
+ return {
+ "CallExpression:exit"(node) {
+ if (!node.callee || node.callee.type !== "MemberExpression") {
+ return;
+ }
+
+ const callee = node.callee;
+ let parent = callee.object;
+ let depth = 1;
+
+ while (parent && parent.callee) {
+ depth += 1;
+ parent = parent.callee.object;
+ }
+
+ if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) {
+ context.report({
+ node: callee.property,
+ loc: callee.property.loc.start,
+ message: "Expected line break before `{{callee}}`.",
+ data: {
+ callee: getPropertyText(callee)
+ },
+ fix(fixer) {
+ const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken);
+
+ return fixer.insertTextBefore(firstTokenAfterObject, "\n");
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-alert.js b/tools/node_modules/eslint/lib/rules/no-alert.js
new file mode 100644
index 0000000000..bc1087253b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-alert.js
@@ -0,0 +1,123 @@
+/**
+ * @fileoverview Rule to flag use of alert, confirm, prompt
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const getPropertyName = require("../ast-utils").getStaticPropertyName;
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks if the given name is a prohibited identifier.
+ * @param {string} name The name to check
+ * @returns {boolean} Whether or not the name is prohibited.
+ */
+function isProhibitedIdentifier(name) {
+ return /^(alert|confirm|prompt)$/.test(name);
+}
+
+/**
+ * Reports the given node and identifier name.
+ * @param {RuleContext} context The ESLint rule context.
+ * @param {ASTNode} node The node to report on.
+ * @param {string} identifierName The name of the identifier.
+ * @returns {void}
+ */
+function report(context, node, identifierName) {
+ context.report(node, "Unexpected {{name}}.", { name: identifierName });
+}
+
+/**
+ * Finds the eslint-scope reference in the given scope.
+ * @param {Object} scope The scope to search.
+ * @param {ASTNode} node The identifier node.
+ * @returns {Reference|null} Returns the found reference or null if none were found.
+ */
+function findReference(scope, node) {
+ const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] &&
+ reference.identifier.range[1] === node.range[1]);
+
+ if (references.length === 1) {
+ return references[0];
+ }
+ return null;
+}
+
+/**
+ * Checks if the given identifier node is shadowed in the given scope.
+ * @param {Object} scope The current scope.
+ * @param {string} node The identifier node to check
+ * @returns {boolean} Whether or not the name is shadowed.
+ */
+function isShadowed(scope, node) {
+ const reference = findReference(scope, node);
+
+ return reference && reference.resolved && reference.resolved.defs.length > 0;
+}
+
+/**
+ * Checks if the given identifier node is a ThisExpression in the global scope or the global window property.
+ * @param {Object} scope The current scope.
+ * @param {string} node The identifier node to check
+ * @returns {boolean} Whether or not the node is a reference to the global object.
+ */
+function isGlobalThisReferenceOrGlobalWindow(scope, node) {
+ if (scope.type === "global" && node.type === "ThisExpression") {
+ return true;
+ }
+ if (node.name === "window") {
+ return !isShadowed(scope, node);
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `alert`, `confirm`, and `prompt`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ return {
+ CallExpression(node) {
+ const callee = node.callee,
+ currentScope = context.getScope();
+
+ // without window.
+ if (callee.type === "Identifier") {
+ const identifierName = callee.name;
+
+ if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) {
+ report(context, node, identifierName);
+ }
+
+ } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) {
+ const identifierName = getPropertyName(callee);
+
+ if (isProhibitedIdentifier(identifierName)) {
+ report(context, node, identifierName);
+ }
+ }
+
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-array-constructor.js b/tools/node_modules/eslint/lib/rules/no-array-constructor.js
new file mode 100644
index 0000000000..70dc8b4cd5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-array-constructor.js
@@ -0,0 +1,47 @@
+/**
+ * @fileoverview Disallow construction of dense arrays using the Array constructor
+ * @author Matt DuVall <http://www.mattduvall.com/>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `Array` constructors",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Disallow construction of dense arrays using the Array constructor
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function check(node) {
+ if (
+ node.arguments.length !== 1 &&
+ node.callee.type === "Identifier" &&
+ node.callee.name === "Array"
+ ) {
+ context.report({ node, message: "The array literal notation [] is preferrable." });
+ }
+ }
+
+ return {
+ CallExpression: check,
+ NewExpression: check
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-await-in-loop.js b/tools/node_modules/eslint/lib/rules/no-await-in-loop.js
new file mode 100644
index 0000000000..d1ed92b704
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-await-in-loop.js
@@ -0,0 +1,83 @@
+/**
+ * @fileoverview Rule to disallow uses of await inside of loops.
+ * @author Nat Mote (nmote)
+ */
+"use strict";
+
+// Node types which are considered loops.
+const loopTypes = new Set([
+ "ForStatement",
+ "ForOfStatement",
+ "ForInStatement",
+ "WhileStatement",
+ "DoWhileStatement"
+]);
+
+/*
+ * Node types at which we should stop looking for loops. For example, it is fine to declare an async
+ * function within a loop, and use await inside of that.
+ */
+const boundaryTypes = new Set([
+ "FunctionDeclaration",
+ "FunctionExpression",
+ "ArrowFunctionExpression"
+]);
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `await` inside of loops",
+ category: "Possible Errors",
+ recommended: false
+ },
+ schema: []
+ },
+ create(context) {
+ return {
+ AwaitExpression(node) {
+ const ancestors = context.getAncestors();
+
+ // Reverse so that we can traverse from the deepest node upwards.
+ ancestors.reverse();
+
+ /*
+ * Create a set of all the ancestors plus this node so that we can check
+ * if this use of await appears in the body of the loop as opposed to
+ * the right-hand side of a for...of, for example.
+ */
+ const ancestorSet = new Set(ancestors).add(node);
+
+ for (let i = 0; i < ancestors.length; i++) {
+ const ancestor = ancestors[i];
+
+ if (boundaryTypes.has(ancestor.type)) {
+
+ /*
+ * Short-circuit out if we encounter a boundary type. Loops above
+ * this do not matter.
+ */
+ return;
+ }
+ if (loopTypes.has(ancestor.type)) {
+
+ /*
+ * Only report if we are actually in the body or another part that gets executed on
+ * every iteration.
+ */
+ if (
+ ancestorSet.has(ancestor.body) ||
+ ancestorSet.has(ancestor.test) ||
+ ancestorSet.has(ancestor.update)
+ ) {
+ context.report({
+ node,
+ message: "Unexpected `await` inside a loop."
+ });
+ return;
+ }
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-bitwise.js b/tools/node_modules/eslint/lib/rules/no-bitwise.js
new file mode 100644
index 0000000000..f062ad2669
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-bitwise.js
@@ -0,0 +1,111 @@
+/**
+ * @fileoverview Rule to flag bitwise identifiers
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+/*
+ *
+ * Set of bitwise operators.
+ *
+ */
+const BITWISE_OPERATORS = [
+ "^", "|", "&", "<<", ">>", ">>>",
+ "^=", "|=", "&=", "<<=", ">>=", ">>>=",
+ "~"
+];
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow bitwise operators",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allow: {
+ type: "array",
+ items: {
+ enum: BITWISE_OPERATORS
+ },
+ uniqueItems: true
+ },
+ int32Hint: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const allowed = options.allow || [];
+ const int32Hint = options.int32Hint === true;
+
+ /**
+ * Reports an unexpected use of a bitwise operator.
+ * @param {ASTNode} node Node which contains the bitwise operator.
+ * @returns {void}
+ */
+ function report(node) {
+ context.report({ node, message: "Unexpected use of '{{operator}}'.", data: { operator: node.operator } });
+ }
+
+ /**
+ * Checks if the given node has a bitwise operator.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} Whether or not the node has a bitwise operator.
+ */
+ function hasBitwiseOperator(node) {
+ return BITWISE_OPERATORS.indexOf(node.operator) !== -1;
+ }
+
+ /**
+ * Checks if exceptions were provided, e.g. `{ allow: ['~', '|'] }`.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} Whether or not the node has a bitwise operator.
+ */
+ function allowedOperator(node) {
+ return allowed.indexOf(node.operator) !== -1;
+ }
+
+ /**
+ * Checks if the given bitwise operator is used for integer typecasting, i.e. "|0"
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} whether the node is used in integer typecasting.
+ */
+ function isInt32Hint(node) {
+ return int32Hint && node.operator === "|" && node.right &&
+ node.right.type === "Literal" && node.right.value === 0;
+ }
+
+ /**
+ * Report if the given node contains a bitwise operator.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkNodeForBitwiseOperator(node) {
+ if (hasBitwiseOperator(node) && !allowedOperator(node) && !isInt32Hint(node)) {
+ report(node);
+ }
+ }
+
+ return {
+ AssignmentExpression: checkNodeForBitwiseOperator,
+ BinaryExpression: checkNodeForBitwiseOperator,
+ UnaryExpression: checkNodeForBitwiseOperator
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-buffer-constructor.js b/tools/node_modules/eslint/lib/rules/no-buffer-constructor.js
new file mode 100644
index 0000000000..1521ff2847
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-buffer-constructor.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview disallow use of the Buffer() constructor
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow use of the Buffer() constructor",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+ schema: []
+ },
+
+ create(context) {
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ "CallExpression[callee.name='Buffer'], NewExpression[callee.name='Buffer']"(node) {
+ context.report({
+ node,
+ message: "{{example}} is deprecated. Use Buffer.from(), Buffer.alloc(), or Buffer.allocUnsafe() instead.",
+ data: { example: node.type === "CallExpression" ? "Buffer()" : "new Buffer()" }
+ });
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-caller.js b/tools/node_modules/eslint/lib/rules/no-caller.js
new file mode 100644
index 0000000000..55a37b7d86
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-caller.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Rule to flag use of arguments.callee and arguments.caller.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `arguments.caller` or `arguments.callee`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ MemberExpression(node) {
+ const objectName = node.object.name,
+ propertyName = node.property.name;
+
+ if (objectName === "arguments" && !node.computed && propertyName && propertyName.match(/^calle[er]$/)) {
+ context.report({ node, message: "Avoid arguments.{{property}}.", data: { property: propertyName } });
+ }
+
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-case-declarations.js b/tools/node_modules/eslint/lib/rules/no-case-declarations.js
new file mode 100644
index 0000000000..e801c6bb6e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-case-declarations.js
@@ -0,0 +1,57 @@
+/**
+ * @fileoverview Rule to flag use of an lexical declarations inside a case clause
+ * @author Erik Arvidsson
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow lexical declarations in case clauses",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Checks whether or not a node is a lexical declaration.
+ * @param {ASTNode} node A direct child statement of a switch case.
+ * @returns {boolean} Whether or not the node is a lexical declaration.
+ */
+ function isLexicalDeclaration(node) {
+ switch (node.type) {
+ case "FunctionDeclaration":
+ case "ClassDeclaration":
+ return true;
+ case "VariableDeclaration":
+ return node.kind !== "var";
+ default:
+ return false;
+ }
+ }
+
+ return {
+ SwitchCase(node) {
+ for (let i = 0; i < node.consequent.length; i++) {
+ const statement = node.consequent[i];
+
+ if (isLexicalDeclaration(statement)) {
+ context.report({
+ node,
+ message: "Unexpected lexical declaration in case block."
+ });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-catch-shadow.js b/tools/node_modules/eslint/lib/rules/no-catch-shadow.js
new file mode 100644
index 0000000000..bef61902e9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-catch-shadow.js
@@ -0,0 +1,69 @@
+/**
+ * @fileoverview Rule to flag variable leak in CatchClauses in IE 8 and earlier
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `catch` clause parameters from shadowing variables in the outer scope",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Check if the parameters are been shadowed
+ * @param {Object} scope current scope
+ * @param {string} name parameter name
+ * @returns {boolean} True is its been shadowed
+ */
+ function paramIsShadowing(scope, name) {
+ return astUtils.getVariableByName(scope, name) !== null;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+
+ CatchClause(node) {
+ let scope = context.getScope();
+
+ /*
+ * When ecmaVersion >= 6, CatchClause creates its own scope
+ * so start from one upper scope to exclude the current node
+ */
+ if (scope.block === node) {
+ scope = scope.upper;
+ }
+
+ if (paramIsShadowing(scope, node.param.name)) {
+ context.report({ node, message: "Value of '{{name}}' may be overwritten in IE 8 and earlier.", data: { name: node.param.name } });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-class-assign.js b/tools/node_modules/eslint/lib/rules/no-class-assign.js
new file mode 100644
index 0000000000..4b0443abc7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-class-assign.js
@@ -0,0 +1,54 @@
+/**
+ * @fileoverview A rule to disallow modifying variables of class declarations
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow reassigning class members",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Finds and reports references that are non initializer and writable.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ astUtils.getModifyingReferences(variable.references).forEach(reference => {
+ context.report({ node: reference.identifier, message: "'{{name}}' is a class.", data: { name: reference.identifier.name } });
+
+ });
+ }
+
+ /**
+ * Finds and reports references that are non initializer and writable.
+ * @param {ASTNode} node - A ClassDeclaration/ClassExpression node to check.
+ * @returns {void}
+ */
+ function checkForClass(node) {
+ context.getDeclaredVariables(node).forEach(checkVariable);
+ }
+
+ return {
+ ClassDeclaration: checkForClass,
+ ClassExpression: checkForClass
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-compare-neg-zero.js b/tools/node_modules/eslint/lib/rules/no-compare-neg-zero.js
new file mode 100644
index 0000000000..604e221919
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-compare-neg-zero.js
@@ -0,0 +1,53 @@
+/**
+ * @fileoverview The rule should warn against code that tries to compare against -0.
+ * @author Aladdin-ADD <hh_2013@foxmail.com>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow comparing against -0",
+ category: "Possible Errors",
+ recommended: true
+ },
+ fixable: null,
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Checks a given node is -0
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is -0.
+ */
+ function isNegZero(node) {
+ return node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "Literal" && node.argument.value === 0;
+ }
+ const OPERATORS_TO_CHECK = new Set([">", ">=", "<", "<=", "==", "===", "!=", "!=="]);
+
+ return {
+ BinaryExpression(node) {
+ if (OPERATORS_TO_CHECK.has(node.operator)) {
+ if (isNegZero(node.left) || isNegZero(node.right)) {
+ context.report({
+ node,
+ message: "Do not use the '{{operator}}' operator to compare against -0.",
+ data: { operator: node.operator }
+ });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-cond-assign.js b/tools/node_modules/eslint/lib/rules/no-cond-assign.js
new file mode 100644
index 0000000000..7c031c13f0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-cond-assign.js
@@ -0,0 +1,139 @@
+/**
+ * @fileoverview Rule to flag assignment in a conditional statement's test expression
+ * @author Stephen Murray <spmurrayzzz>
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+const NODE_DESCRIPTIONS = {
+ DoWhileStatement: "a 'do...while' statement",
+ ForStatement: "a 'for' statement",
+ IfStatement: "an 'if' statement",
+ WhileStatement: "a 'while' statement"
+};
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow assignment operators in conditional expressions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ enum: ["except-parens", "always"]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const prohibitAssign = (context.options[0] || "except-parens");
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Check whether an AST node is the test expression for a conditional statement.
+ * @param {!Object} node The node to test.
+ * @returns {boolean} `true` if the node is the text expression for a conditional statement; otherwise, `false`.
+ */
+ function isConditionalTestExpression(node) {
+ return node.parent &&
+ node.parent.test &&
+ node === node.parent.test;
+ }
+
+ /**
+ * Given an AST node, perform a bottom-up search for the first ancestor that represents a conditional statement.
+ * @param {!Object} node The node to use at the start of the search.
+ * @returns {?Object} The closest ancestor node that represents a conditional statement.
+ */
+ function findConditionalAncestor(node) {
+ let currentAncestor = node;
+
+ do {
+ if (isConditionalTestExpression(currentAncestor)) {
+ return currentAncestor.parent;
+ }
+ } while ((currentAncestor = currentAncestor.parent) && !astUtils.isFunction(currentAncestor));
+
+ return null;
+ }
+
+ /**
+ * Check whether the code represented by an AST node is enclosed in two sets of parentheses.
+ * @param {!Object} node The node to test.
+ * @returns {boolean} `true` if the code is enclosed in two sets of parentheses; otherwise, `false`.
+ */
+ function isParenthesisedTwice(node) {
+ const previousToken = sourceCode.getTokenBefore(node, 1),
+ nextToken = sourceCode.getTokenAfter(node, 1);
+
+ return astUtils.isParenthesised(sourceCode, node) &&
+ astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
+ astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
+ }
+
+ /**
+ * Check a conditional statement's test expression for top-level assignments that are not enclosed in parentheses.
+ * @param {!Object} node The node for the conditional statement.
+ * @returns {void}
+ */
+ function testForAssign(node) {
+ if (node.test &&
+ (node.test.type === "AssignmentExpression") &&
+ (node.type === "ForStatement"
+ ? !astUtils.isParenthesised(sourceCode, node.test)
+ : !isParenthesisedTwice(node.test)
+ )
+ ) {
+
+ // must match JSHint's error message
+ context.report({
+ node,
+ loc: node.test.loc.start,
+ message: "Expected a conditional expression and instead saw an assignment."
+ });
+ }
+ }
+
+ /**
+ * Check whether an assignment expression is descended from a conditional statement's test expression.
+ * @param {!Object} node The node for the assignment expression.
+ * @returns {void}
+ */
+ function testForConditionalAncestor(node) {
+ const ancestor = findConditionalAncestor(node);
+
+ if (ancestor) {
+ context.report({
+ node: ancestor,
+ message: "Unexpected assignment within {{type}}.",
+ data: {
+ type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type
+ }
+ });
+ }
+ }
+
+ if (prohibitAssign === "always") {
+ return {
+ AssignmentExpression: testForConditionalAncestor
+ };
+ }
+
+ return {
+ DoWhileStatement: testForAssign,
+ ForStatement: testForAssign,
+ IfStatement: testForAssign,
+ WhileStatement: testForAssign
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-confusing-arrow.js b/tools/node_modules/eslint/lib/rules/no-confusing-arrow.js
new file mode 100644
index 0000000000..fc69ca39a9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-confusing-arrow.js
@@ -0,0 +1,76 @@
+/**
+ * @fileoverview A rule to warn against using arrow functions when they could be
+ * confused with comparisions
+ * @author Jxck <https://github.com/Jxck>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils.js");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a node is a conditional expression.
+ * @param {ASTNode} node - node to test
+ * @returns {boolean} `true` if the node is a conditional expression.
+ */
+function isConditional(node) {
+ return node && node.type === "ConditionalExpression";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow arrow functions where they could be confused with comparisons",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: [{
+ type: "object",
+ properties: {
+ allowParens: { type: "boolean" }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+ const config = context.options[0] || {};
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports if an arrow function contains an ambiguous conditional.
+ * @param {ASTNode} node - A node to check and report.
+ * @returns {void}
+ */
+ function checkArrowFunc(node) {
+ const body = node.body;
+
+ if (isConditional(body) && !(config.allowParens && astUtils.isParenthesised(sourceCode, body))) {
+ context.report({
+ node,
+ message: "Arrow function used ambiguously with a conditional expression.",
+ fix(fixer) {
+
+ // if `allowParens` is not set to true dont bother wrapping in parens
+ return config.allowParens && fixer.replaceText(node.body, `(${sourceCode.getText(node.body)})`);
+ }
+ });
+ }
+ }
+
+ return {
+ ArrowFunctionExpression: checkArrowFunc
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-console.js b/tools/node_modules/eslint/lib/rules/no-console.js
new file mode 100644
index 0000000000..f5a3a235e6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-console.js
@@ -0,0 +1,131 @@
+/**
+ * @fileoverview Rule to flag use of console object
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `console`",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allow: {
+ type: "array",
+ items: {
+ type: "string"
+ },
+ minItems: 1,
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const allowed = options.allow || [];
+
+ /**
+ * Checks whether the given reference is 'console' or not.
+ *
+ * @param {eslint-scope.Reference} reference - The reference to check.
+ * @returns {boolean} `true` if the reference is 'console'.
+ */
+ function isConsole(reference) {
+ const id = reference.identifier;
+
+ return id && id.name === "console";
+ }
+
+ /**
+ * Checks whether the property name of the given MemberExpression node
+ * is allowed by options or not.
+ *
+ * @param {ASTNode} node - The MemberExpression node to check.
+ * @returns {boolean} `true` if the property name of the node is allowed.
+ */
+ function isAllowed(node) {
+ const propertyName = astUtils.getStaticPropertyName(node);
+
+ return propertyName && allowed.indexOf(propertyName) !== -1;
+ }
+
+ /**
+ * Checks whether the given reference is a member access which is not
+ * allowed by options or not.
+ *
+ * @param {eslint-scope.Reference} reference - The reference to check.
+ * @returns {boolean} `true` if the reference is a member access which
+ * is not allowed by options.
+ */
+ function isMemberAccessExceptAllowed(reference) {
+ const node = reference.identifier;
+ const parent = node.parent;
+
+ return (
+ parent.type === "MemberExpression" &&
+ parent.object === node &&
+ !isAllowed(parent)
+ );
+ }
+
+ /**
+ * Reports the given reference as a violation.
+ *
+ * @param {eslint-scope.Reference} reference - The reference to report.
+ * @returns {void}
+ */
+ function report(reference) {
+ const node = reference.identifier.parent;
+
+ context.report({
+ node,
+ loc: node.loc,
+ message: "Unexpected console statement."
+ });
+ }
+
+ return {
+ "Program:exit"() {
+ const scope = context.getScope();
+ const consoleVar = astUtils.getVariableByName(scope, "console");
+ const shadowed = consoleVar && consoleVar.defs.length > 0;
+
+ /*
+ * 'scope.through' includes all references to undefined
+ * variables. If the variable 'console' is not defined, it uses
+ * 'scope.through'.
+ */
+ const references = consoleVar
+ ? consoleVar.references
+ : scope.through.filter(isConsole);
+
+ if (!shadowed) {
+ references
+ .filter(isMemberAccessExceptAllowed)
+ .forEach(report);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-const-assign.js b/tools/node_modules/eslint/lib/rules/no-const-assign.js
new file mode 100644
index 0000000000..db1848a981
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-const-assign.js
@@ -0,0 +1,47 @@
+/**
+ * @fileoverview A rule to disallow modifying variables that are declared using `const`
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow reassigning `const` variables",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Finds and reports references that are non initializer and writable.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ astUtils.getModifyingReferences(variable.references).forEach(reference => {
+ context.report({ node: reference.identifier, message: "'{{name}}' is constant.", data: { name: reference.identifier.name } });
+ });
+ }
+
+ return {
+ VariableDeclaration(node) {
+ if (node.kind === "const") {
+ context.getDeclaredVariables(node).forEach(checkVariable);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-constant-condition.js b/tools/node_modules/eslint/lib/rules/no-constant-condition.js
new file mode 100644
index 0000000000..0cd445dfdb
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-constant-condition.js
@@ -0,0 +1,210 @@
+/**
+ * @fileoverview Rule to flag use constant conditions
+ * @author Christian Schulz <http://rndm.de>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow constant expressions in conditions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ checkLoops: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {},
+ checkLoops = options.checkLoops !== false,
+ loopSetStack = [];
+
+ let loopsInCurrentScope = new Set();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+
+ /**
+ * Checks if a branch node of LogicalExpression short circuits the whole condition
+ * @param {ASTNode} node The branch of main condition which needs to be checked
+ * @param {string} operator The operator of the main LogicalExpression.
+ * @returns {boolean} true when condition short circuits whole condition
+ */
+ function isLogicalIdentity(node, operator) {
+ switch (node.type) {
+ case "Literal":
+ return (operator === "||" && node.value === true) ||
+ (operator === "&&" && node.value === false);
+
+ case "UnaryExpression":
+ return (operator === "&&" && node.operator === "void");
+
+ case "LogicalExpression":
+ return isLogicalIdentity(node.left, node.operator) ||
+ isLogicalIdentity(node.right, node.operator);
+
+ // no default
+ }
+ return false;
+ }
+
+ /**
+ * Checks if a node has a constant truthiness value.
+ * @param {ASTNode} node The AST node to check.
+ * @param {boolean} inBooleanPosition `false` if checking branch of a condition.
+ * `true` in all other cases
+ * @returns {Bool} true when node's truthiness is constant
+ * @private
+ */
+ function isConstant(node, inBooleanPosition) {
+ switch (node.type) {
+ case "Literal":
+ case "ArrowFunctionExpression":
+ case "FunctionExpression":
+ case "ObjectExpression":
+ case "ArrayExpression":
+ return true;
+
+ case "UnaryExpression":
+ if (node.operator === "void") {
+ return true;
+ }
+
+ return (node.operator === "typeof" && inBooleanPosition) ||
+ isConstant(node.argument, true);
+
+ case "BinaryExpression":
+ return isConstant(node.left, false) &&
+ isConstant(node.right, false) &&
+ node.operator !== "in";
+
+ case "LogicalExpression": {
+ const isLeftConstant = isConstant(node.left, inBooleanPosition);
+ const isRightConstant = isConstant(node.right, inBooleanPosition);
+ const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
+ const isRightShortCircuit = (isRightConstant && isLogicalIdentity(node.right, node.operator));
+
+ return (isLeftConstant && isRightConstant) || isLeftShortCircuit || isRightShortCircuit;
+ }
+
+ case "AssignmentExpression":
+ return (node.operator === "=") && isConstant(node.right, inBooleanPosition);
+
+ case "SequenceExpression":
+ return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);
+
+ // no default
+ }
+ return false;
+ }
+
+ /**
+ * Tracks when the given node contains a constant condition.
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ * @private
+ */
+ function trackConstantConditionLoop(node) {
+ if (node.test && isConstant(node.test, true)) {
+ loopsInCurrentScope.add(node);
+ }
+ }
+
+ /**
+ * Reports when the set contains the given constant condition node
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkConstantConditionLoopInSet(node) {
+ if (loopsInCurrentScope.has(node)) {
+ loopsInCurrentScope.delete(node);
+ context.report({ node: node.test, message: "Unexpected constant condition." });
+ }
+ }
+
+ /**
+ * Reports when the given node contains a constant condition.
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ * @private
+ */
+ function reportIfConstant(node) {
+ if (node.test && isConstant(node.test, true)) {
+ context.report({ node: node.test, message: "Unexpected constant condition." });
+ }
+ }
+
+ /**
+ * Stores current set of constant loops in loopSetStack temporarily
+ * and uses a new set to track constant loops
+ * @returns {void}
+ * @private
+ */
+ function enterFunction() {
+ loopSetStack.push(loopsInCurrentScope);
+ loopsInCurrentScope = new Set();
+ }
+
+ /**
+ * Reports when the set still contains stored constant conditions
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ * @private
+ */
+ function exitFunction() {
+ loopsInCurrentScope = loopSetStack.pop();
+ }
+
+ /**
+ * Checks node when checkLoops option is enabled
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkLoop(node) {
+ if (checkLoops) {
+ trackConstantConditionLoop(node);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ ConditionalExpression: reportIfConstant,
+ IfStatement: reportIfConstant,
+ WhileStatement: checkLoop,
+ "WhileStatement:exit": checkConstantConditionLoopInSet,
+ DoWhileStatement: checkLoop,
+ "DoWhileStatement:exit": checkConstantConditionLoopInSet,
+ ForStatement: checkLoop,
+ "ForStatement > .test": node => checkLoop(node.parent),
+ "ForStatement:exit": checkConstantConditionLoopInSet,
+ FunctionDeclaration: enterFunction,
+ "FunctionDeclaration:exit": exitFunction,
+ YieldExpression: () => loopsInCurrentScope.clear()
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-continue.js b/tools/node_modules/eslint/lib/rules/no-continue.js
new file mode 100644
index 0000000000..2615fba13e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-continue.js
@@ -0,0 +1,32 @@
+/**
+ * @fileoverview Rule to flag use of continue statement
+ * @author Borislav Zhivkov
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `continue` statements",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ ContinueStatement(node) {
+ context.report({ node, message: "Unexpected use of continue statement." });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-control-regex.js b/tools/node_modules/eslint/lib/rules/no-control-regex.js
new file mode 100644
index 0000000000..14981f4ab1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-control-regex.js
@@ -0,0 +1,127 @@
+/**
+ * @fileoverview Rule to forbid control charactes from regular expressions.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow control characters in regular expressions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Get the regex expression
+ * @param {ASTNode} node node to evaluate
+ * @returns {*} Regex if found else null
+ * @private
+ */
+ function getRegExp(node) {
+ if (node.value instanceof RegExp) {
+ return node.value;
+ }
+ if (typeof node.value === "string") {
+
+ const parent = context.getAncestors().pop();
+
+ if ((parent.type === "NewExpression" || parent.type === "CallExpression") &&
+ parent.callee.type === "Identifier" && parent.callee.name === "RegExp"
+ ) {
+
+ // there could be an invalid regular expression string
+ try {
+ return new RegExp(node.value);
+ } catch (ex) {
+ return null;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ const controlChar = /[\x00-\x1f]/g; // eslint-disable-line no-control-regex
+ const consecutiveSlashes = /\\+/g;
+ const consecutiveSlashesAtEnd = /\\+$/g;
+ const stringControlChar = /\\x[01][0-9a-f]/ig;
+ const stringControlCharWithoutSlash = /x[01][0-9a-f]/ig;
+
+ /**
+ * Return a list of the control characters in the given regex string
+ * @param {string} regexStr regex as string to check
+ * @returns {array} returns a list of found control characters on given string
+ * @private
+ */
+ function getControlCharacters(regexStr) {
+
+ // check control characters, if RegExp object used
+ const controlChars = regexStr.match(controlChar) || [];
+
+ let stringControlChars = [];
+
+ // check substr, if regex literal used
+ const subStrIndex = regexStr.search(stringControlChar);
+
+ if (subStrIndex > -1) {
+
+ // is it escaped, check backslash count
+ const possibleEscapeCharacters = regexStr.slice(0, subStrIndex).match(consecutiveSlashesAtEnd);
+
+ const hasControlChars = possibleEscapeCharacters === null || !(possibleEscapeCharacters[0].length % 2);
+
+ if (hasControlChars) {
+ stringControlChars = regexStr.slice(subStrIndex, -1)
+ .split(consecutiveSlashes)
+ .filter(Boolean)
+ .map(x => {
+ const match = x.match(stringControlCharWithoutSlash) || [x];
+
+ return `\\${match[0]}`;
+ });
+ }
+ }
+
+ return controlChars.map(x => {
+ const hexCode = `0${x.charCodeAt(0).toString(16)}`.slice(-2);
+
+ return `\\x${hexCode}`;
+ }).concat(stringControlChars);
+ }
+
+ return {
+ Literal(node) {
+ const regex = getRegExp(node);
+
+ if (regex) {
+ const computedValue = regex.toString();
+
+ const controlCharacters = getControlCharacters(computedValue);
+
+ if (controlCharacters.length > 0) {
+ context.report({
+ node,
+ message: "Unexpected control character(s) in regular expression: {{controlChars}}.",
+ data: {
+ controlChars: controlCharacters.join(", ")
+ }
+ });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-debugger.js b/tools/node_modules/eslint/lib/rules/no-debugger.js
new file mode 100644
index 0000000000..d79cb18166
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-debugger.js
@@ -0,0 +1,43 @@
+/**
+ * @fileoverview Rule to flag use of a debugger statement
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `debugger`",
+ category: "Possible Errors",
+ recommended: true
+ },
+ fixable: "code",
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ DebuggerStatement(node) {
+ context.report({
+ node,
+ message: "Unexpected 'debugger' statement.",
+ fix(fixer) {
+ if (astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
+ return fixer.remove(node);
+ }
+ return null;
+ }
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-delete-var.js b/tools/node_modules/eslint/lib/rules/no-delete-var.js
new file mode 100644
index 0000000000..adc1b5bb9c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-delete-var.js
@@ -0,0 +1,35 @@
+/**
+ * @fileoverview Rule to flag when deleting variables
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow deleting variables",
+ category: "Variables",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ UnaryExpression(node) {
+ if (node.operator === "delete" && node.argument.type === "Identifier") {
+ context.report({ node, message: "Variables should not be deleted." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-div-regex.js b/tools/node_modules/eslint/lib/rules/no-div-regex.js
new file mode 100644
index 0000000000..84a9b9a3aa
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-div-regex.js
@@ -0,0 +1,38 @@
+/**
+ * @fileoverview Rule to check for ambiguous div operator in regexes
+ * @author Matt DuVall <http://www.mattduvall.com>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow division operators explicitly at the beginning of regular expressions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+
+ Literal(node) {
+ const token = sourceCode.getFirstToken(node);
+
+ if (token.type === "RegularExpression" && token.value[1] === "=") {
+ context.report({ node, message: "A regular expression literal can be confused with '/='." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-args.js b/tools/node_modules/eslint/lib/rules/no-dupe-args.js
new file mode 100644
index 0000000000..c932be01d7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-dupe-args.js
@@ -0,0 +1,73 @@
+/**
+ * @fileoverview Rule to flag duplicate arguments
+ * @author Jamund Ferguson
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow duplicate arguments in `function` definitions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Checks whether or not a given definition is a parameter's.
+ * @param {eslint-scope.DefEntry} def - A definition to check.
+ * @returns {boolean} `true` if the definition is a parameter's.
+ */
+ function isParameter(def) {
+ return def.type === "Parameter";
+ }
+
+ /**
+ * Determines if a given node has duplicate parameters.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkParams(node) {
+ const variables = context.getDeclaredVariables(node);
+
+ for (let i = 0; i < variables.length; ++i) {
+ const variable = variables[i];
+
+ // Checks and reports duplications.
+ const defs = variable.defs.filter(isParameter);
+
+ if (defs.length >= 2) {
+ context.report({
+ node,
+ message: "Duplicate param '{{name}}'.",
+ data: { name: variable.name }
+ });
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ FunctionDeclaration: checkParams,
+ FunctionExpression: checkParams
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-class-members.js b/tools/node_modules/eslint/lib/rules/no-dupe-class-members.js
new file mode 100644
index 0000000000..07b999fab1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-dupe-class-members.js
@@ -0,0 +1,109 @@
+/**
+ * @fileoverview A rule to disallow duplicate name in class members.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow duplicate class members",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ let stack = [];
+
+ /**
+ * Gets state of a given member name.
+ * @param {string} name - A name of a member.
+ * @param {boolean} isStatic - A flag which specifies that is a static member.
+ * @returns {Object} A state of a given member name.
+ * - retv.init {boolean} A flag which shows the name is declared as normal member.
+ * - retv.get {boolean} A flag which shows the name is declared as getter.
+ * - retv.set {boolean} A flag which shows the name is declared as setter.
+ */
+ function getState(name, isStatic) {
+ const stateMap = stack[stack.length - 1];
+ const key = `$${name}`; // to avoid "__proto__".
+
+ if (!stateMap[key]) {
+ stateMap[key] = {
+ nonStatic: { init: false, get: false, set: false },
+ static: { init: false, get: false, set: false }
+ };
+ }
+
+ return stateMap[key][isStatic ? "static" : "nonStatic"];
+ }
+
+ /**
+ * Gets the name text of a given node.
+ *
+ * @param {ASTNode} node - A node to get the name.
+ * @returns {string} The name text of the node.
+ */
+ function getName(node) {
+ switch (node.type) {
+ case "Identifier": return node.name;
+ case "Literal": return String(node.value);
+
+ /* istanbul ignore next: syntax error */
+ default: return "";
+ }
+ }
+
+ return {
+
+ // Initializes the stack of state of member declarations.
+ Program() {
+ stack = [];
+ },
+
+ // Initializes state of member declarations for the class.
+ ClassBody() {
+ stack.push(Object.create(null));
+ },
+
+ // Disposes the state for the class.
+ "ClassBody:exit"() {
+ stack.pop();
+ },
+
+ // Reports the node if its name has been declared already.
+ MethodDefinition(node) {
+ if (node.computed) {
+ return;
+ }
+
+ const name = getName(node.key);
+ const state = getState(name, node.static);
+ let isDuplicate = false;
+
+ if (node.kind === "get") {
+ isDuplicate = (state.init || state.get);
+ state.get = true;
+ } else if (node.kind === "set") {
+ isDuplicate = (state.init || state.set);
+ state.set = true;
+ } else {
+ isDuplicate = (state.init || state.get || state.set);
+ state.init = true;
+ }
+
+ if (isDuplicate) {
+ context.report({ node, message: "Duplicate name '{{name}}'.", data: { name } });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-keys.js b/tools/node_modules/eslint/lib/rules/no-dupe-keys.js
new file mode 100644
index 0000000000..0120d0b38c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-dupe-keys.js
@@ -0,0 +1,135 @@
+/**
+ * @fileoverview Rule to flag use of duplicate keys in an object.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const GET_KIND = /^(?:init|get)$/;
+const SET_KIND = /^(?:init|set)$/;
+
+/**
+ * The class which stores properties' information of an object.
+ */
+class ObjectInfo {
+
+ /**
+ * @param {ObjectInfo|null} upper - The information of the outer object.
+ * @param {ASTNode} node - The ObjectExpression node of this information.
+ */
+ constructor(upper, node) {
+ this.upper = upper;
+ this.node = node;
+ this.properties = new Map();
+ }
+
+ /**
+ * Gets the information of the given Property node.
+ * @param {ASTNode} node - The Property node to get.
+ * @returns {{get: boolean, set: boolean}} The information of the property.
+ */
+ getPropertyInfo(node) {
+ const name = astUtils.getStaticPropertyName(node);
+
+ if (!this.properties.has(name)) {
+ this.properties.set(name, { get: false, set: false });
+ }
+ return this.properties.get(name);
+ }
+
+ /**
+ * Checks whether the given property has been defined already or not.
+ * @param {ASTNode} node - The Property node to check.
+ * @returns {boolean} `true` if the property has been defined.
+ */
+ isPropertyDefined(node) {
+ const entry = this.getPropertyInfo(node);
+
+ return (
+ (GET_KIND.test(node.kind) && entry.get) ||
+ (SET_KIND.test(node.kind) && entry.set)
+ );
+ }
+
+ /**
+ * Defines the given property.
+ * @param {ASTNode} node - The Property node to define.
+ * @returns {void}
+ */
+ defineProperty(node) {
+ const entry = this.getPropertyInfo(node);
+
+ if (GET_KIND.test(node.kind)) {
+ entry.get = true;
+ }
+ if (SET_KIND.test(node.kind)) {
+ entry.set = true;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow duplicate keys in object literals",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ let info = null;
+
+ return {
+ ObjectExpression(node) {
+ info = new ObjectInfo(info, node);
+ },
+ "ObjectExpression:exit"() {
+ info = info.upper;
+ },
+
+ Property(node) {
+ const name = astUtils.getStaticPropertyName(node);
+
+ // Skip destructuring.
+ if (node.parent.type !== "ObjectExpression") {
+ return;
+ }
+
+ // Skip if the name is not static.
+ if (!name) {
+ return;
+ }
+
+ // Reports if the name is defined already.
+ if (info.isPropertyDefined(node)) {
+ context.report({
+ node: info.node,
+ loc: node.key.loc,
+ message: "Duplicate key '{{name}}'.",
+ data: { name }
+ });
+ }
+
+ // Update info.
+ info.defineProperty(node);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-duplicate-case.js b/tools/node_modules/eslint/lib/rules/no-duplicate-case.js
new file mode 100644
index 0000000000..07823f284c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-duplicate-case.js
@@ -0,0 +1,43 @@
+/**
+ * @fileoverview Rule to disallow a duplicate case label.
+ * @author Dieter Oberkofler
+ * @author Burak Yigit Kaya
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow duplicate case labels",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ SwitchStatement(node) {
+ const mapping = {};
+
+ node.cases.forEach(switchCase => {
+ const key = sourceCode.getText(switchCase.test);
+
+ if (mapping[key]) {
+ context.report({ node: switchCase, message: "Duplicate case label." });
+ } else {
+ mapping[key] = switchCase;
+ }
+ });
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-duplicate-imports.js b/tools/node_modules/eslint/lib/rules/no-duplicate-imports.js
new file mode 100644
index 0000000000..d12c3a56df
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-duplicate-imports.js
@@ -0,0 +1,137 @@
+/**
+ * @fileoverview Restrict usage of duplicate imports.
+ * @author Simen Bekkhus
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the name of the module imported or re-exported.
+ *
+ * @param {ASTNode} node - A node to get.
+ * @returns {string} the name of the module, or empty string if no name.
+ */
+function getValue(node) {
+ if (node && node.source && node.source.value) {
+ return node.source.value.trim();
+ }
+
+ return "";
+}
+
+/**
+ * Checks if the name of the import or export exists in the given array, and reports if so.
+ *
+ * @param {RuleContext} context - The ESLint rule context object.
+ * @param {ASTNode} node - A node to get.
+ * @param {string} value - The name of the imported or exported module.
+ * @param {string[]} array - The array containing other imports or exports in the file.
+ * @param {string} message - A message to be reported after the name of the module
+ *
+ * @returns {void} No return value
+ */
+function checkAndReport(context, node, value, array, message) {
+ if (array.indexOf(value) !== -1) {
+ context.report({
+ node,
+ message: "'{{module}}' {{message}}",
+ data: {
+ module: value,
+ message
+ }
+ });
+ }
+}
+
+/**
+ * @callback nodeCallback
+ * @param {ASTNode} node - A node to handle.
+ */
+
+/**
+ * Returns a function handling the imports of a given file
+ *
+ * @param {RuleContext} context - The ESLint rule context object.
+ * @param {boolean} includeExports - Whether or not to check for exports in addition to imports.
+ * @param {string[]} importsInFile - The array containing other imports in the file.
+ * @param {string[]} exportsInFile - The array containing other exports in the file.
+ *
+ * @returns {nodeCallback} A function passed to ESLint to handle the statement.
+ */
+function handleImports(context, includeExports, importsInFile, exportsInFile) {
+ return function(node) {
+ const value = getValue(node);
+
+ if (value) {
+ checkAndReport(context, node, value, importsInFile, "import is duplicated.");
+
+ if (includeExports) {
+ checkAndReport(context, node, value, exportsInFile, "import is duplicated as export.");
+ }
+
+ importsInFile.push(value);
+ }
+ };
+}
+
+/**
+ * Returns a function handling the exports of a given file
+ *
+ * @param {RuleContext} context - The ESLint rule context object.
+ * @param {string[]} importsInFile - The array containing other imports in the file.
+ * @param {string[]} exportsInFile - The array containing other exports in the file.
+ *
+ * @returns {nodeCallback} A function passed to ESLint to handle the statement.
+ */
+function handleExports(context, importsInFile, exportsInFile) {
+ return function(node) {
+ const value = getValue(node);
+
+ if (value) {
+ checkAndReport(context, node, value, exportsInFile, "export is duplicated.");
+ checkAndReport(context, node, value, importsInFile, "export is duplicated as import.");
+
+ exportsInFile.push(value);
+ }
+ };
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow duplicate module imports",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [{
+ type: "object",
+ properties: {
+ includeExports: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+ const includeExports = (context.options[0] || {}).includeExports,
+ importsInFile = [],
+ exportsInFile = [];
+
+ const handlers = {
+ ImportDeclaration: handleImports(context, includeExports, importsInFile, exportsInFile)
+ };
+
+ if (includeExports) {
+ handlers.ExportNamedDeclaration = handleExports(context, importsInFile, exportsInFile);
+ handlers.ExportAllDeclaration = handleExports(context, importsInFile, exportsInFile);
+ }
+
+ return handlers;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-else-return.js b/tools/node_modules/eslint/lib/rules/no-else-return.js
new file mode 100644
index 0000000000..deeff41ab8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-else-return.js
@@ -0,0 +1,276 @@
+/**
+ * @fileoverview Rule to flag `else` after a `return` in `if`
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+const FixTracker = require("../util/fix-tracker");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `else` blocks after `return` statements in `if` statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [{
+ type: "object",
+ properties: {
+ allowElseIf: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }],
+ fixable: "code"
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Display the context report if rule is violated
+ *
+ * @param {Node} node The 'else' node
+ * @returns {void}
+ */
+ function displayReport(node) {
+ context.report({
+ node,
+ message: "Unnecessary 'else' after 'return'.",
+ fix: fixer => {
+ const sourceCode = context.getSourceCode();
+ const startToken = sourceCode.getFirstToken(node);
+ const elseToken = sourceCode.getTokenBefore(startToken);
+ const source = sourceCode.getText(node);
+ const lastIfToken = sourceCode.getTokenBefore(elseToken);
+ let fixedSource, firstTokenOfElseBlock;
+
+ if (startToken.type === "Punctuator" && startToken.value === "{") {
+ firstTokenOfElseBlock = sourceCode.getTokenAfter(startToken);
+ } else {
+ firstTokenOfElseBlock = startToken;
+ }
+
+ /*
+ * If the if block does not have curly braces and does not end in a semicolon
+ * and the else block starts with (, [, /, +, ` or -, then it is not
+ * safe to remove the else keyword, because ASI will not add a semicolon
+ * after the if block
+ */
+ const ifBlockMaybeUnsafe = node.parent.consequent.type !== "BlockStatement" && lastIfToken.value !== ";";
+ const elseBlockUnsafe = /^[([/+`-]/.test(firstTokenOfElseBlock.value);
+
+ if (ifBlockMaybeUnsafe && elseBlockUnsafe) {
+ return null;
+ }
+
+ const endToken = sourceCode.getLastToken(node);
+ const lastTokenOfElseBlock = sourceCode.getTokenBefore(endToken);
+
+ if (lastTokenOfElseBlock.value !== ";") {
+ const nextToken = sourceCode.getTokenAfter(endToken);
+
+ const nextTokenUnsafe = nextToken && /^[([/+`-]/.test(nextToken.value);
+ const nextTokenOnSameLine = nextToken && nextToken.loc.start.line === lastTokenOfElseBlock.loc.start.line;
+
+ /*
+ * If the else block contents does not end in a semicolon,
+ * and the else block starts with (, [, /, +, ` or -, then it is not
+ * safe to remove the else block, because ASI will not add a semicolon
+ * after the remaining else block contents
+ */
+ if (nextTokenUnsafe || (nextTokenOnSameLine && nextToken.value !== "}")) {
+ return null;
+ }
+ }
+
+ if (startToken.type === "Punctuator" && startToken.value === "{") {
+ fixedSource = source.slice(1, -1);
+ } else {
+ fixedSource = source;
+ }
+
+ /*
+ * Extend the replacement range to include the entire
+ * function to avoid conflicting with no-useless-return.
+ * https://github.com/eslint/eslint/issues/8026
+ */
+ return new FixTracker(fixer, sourceCode)
+ .retainEnclosingFunction(node)
+ .replaceTextRange([elseToken.range[0], node.range[1]], fixedSource);
+ }
+ });
+ }
+
+ /**
+ * Check to see if the node is a ReturnStatement
+ *
+ * @param {Node} node The node being evaluated
+ * @returns {boolean} True if node is a return
+ */
+ function checkForReturn(node) {
+ return node.type === "ReturnStatement";
+ }
+
+ /**
+ * Naive return checking, does not iterate through the whole
+ * BlockStatement because we make the assumption that the ReturnStatement
+ * will be the last node in the body of the BlockStatement.
+ *
+ * @param {Node} node The consequent/alternate node
+ * @returns {boolean} True if it has a return
+ */
+ function naiveHasReturn(node) {
+ if (node.type === "BlockStatement") {
+ const body = node.body,
+ lastChildNode = body[body.length - 1];
+
+ return lastChildNode && checkForReturn(lastChildNode);
+ }
+ return checkForReturn(node);
+ }
+
+ /**
+ * Check to see if the node is valid for evaluation,
+ * meaning it has an else.
+ *
+ * @param {Node} node The node being evaluated
+ * @returns {boolean} True if the node is valid
+ */
+ function hasElse(node) {
+ return node.alternate && node.consequent;
+ }
+
+ /**
+ * If the consequent is an IfStatement, check to see if it has an else
+ * and both its consequent and alternate path return, meaning this is
+ * a nested case of rule violation. If-Else not considered currently.
+ *
+ * @param {Node} node The consequent node
+ * @returns {boolean} True if this is a nested rule violation
+ */
+ function checkForIf(node) {
+ return node.type === "IfStatement" && hasElse(node) &&
+ naiveHasReturn(node.alternate) && naiveHasReturn(node.consequent);
+ }
+
+ /**
+ * Check the consequent/body node to make sure it is not
+ * a ReturnStatement or an IfStatement that returns on both
+ * code paths.
+ *
+ * @param {Node} node The consequent or body node
+ * @param {Node} alternate The alternate node
+ * @returns {boolean} `true` if it is a Return/If node that always returns.
+ */
+ function checkForReturnOrIf(node) {
+ return checkForReturn(node) || checkForIf(node);
+ }
+
+
+ /**
+ * Check whether a node returns in every codepath.
+ * @param {Node} node The node to be checked
+ * @returns {boolean} `true` if it returns on every codepath.
+ */
+ function alwaysReturns(node) {
+ if (node.type === "BlockStatement") {
+
+ // If we have a BlockStatement, check each consequent body node.
+ return node.body.some(checkForReturnOrIf);
+ }
+
+ /*
+ * If not a block statement, make sure the consequent isn't a
+ * ReturnStatement or an IfStatement with returns on both paths.
+ */
+ return checkForReturnOrIf(node);
+ }
+
+
+ /**
+ * Check the if statement, but don't catch else-if blocks.
+ * @returns {void}
+ * @param {Node} node The node for the if statement to check
+ * @private
+ */
+ function checkIfWithoutElse(node) {
+ const parent = node.parent;
+ let consequents,
+ alternate;
+
+ /*
+ * Fixing this would require splitting one statement into two, so no error should
+ * be reported if this node is in a position where only one statement is allowed.
+ */
+ if (!astUtils.STATEMENT_LIST_PARENTS.has(parent.type)) {
+ return;
+ }
+
+ for (consequents = []; node.type === "IfStatement"; node = node.alternate) {
+ if (!node.alternate) {
+ return;
+ }
+ consequents.push(node.consequent);
+ alternate = node.alternate;
+ }
+
+ if (consequents.every(alwaysReturns)) {
+ displayReport(alternate);
+ }
+ }
+
+ /**
+ * Check the if statement
+ * @returns {void}
+ * @param {Node} node The node for the if statement to check
+ * @private
+ */
+ function checkIfWithElse(node) {
+ const parent = node.parent;
+
+
+ /*
+ * Fixing this would require splitting one statement into two, so no error should
+ * be reported if this node is in a position where only one statement is allowed.
+ */
+ if (!astUtils.STATEMENT_LIST_PARENTS.has(parent.type)) {
+ return;
+ }
+
+ const alternate = node.alternate;
+
+ if (alternate && alwaysReturns(node.consequent)) {
+ displayReport(alternate);
+ }
+ }
+
+ const allowElseIf = !(context.options[0] && context.options[0].allowElseIf === false);
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+
+ "IfStatement:exit": allowElseIf ? checkIfWithoutElse : checkIfWithElse
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-empty-character-class.js b/tools/node_modules/eslint/lib/rules/no-empty-character-class.js
new file mode 100644
index 0000000000..0ea7c5a0d1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-empty-character-class.js
@@ -0,0 +1,57 @@
+/**
+ * @fileoverview Rule to flag the use of empty character classes in regular expressions
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/*
+ * plain-English description of the following regexp:
+ * 0. `^` fix the match at the beginning of the string
+ * 1. `\/`: the `/` that begins the regexp
+ * 2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
+ * 2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
+ * 2.1. `\\.`: an escape sequence
+ * 2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
+ * 3. `\/` the `/` that ends the regexp
+ * 4. `[gimuy]*`: optional regexp flags
+ * 5. `$`: fix the match at the end of the string
+ */
+const regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+])*\/[gimuy]*$/;
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow empty character classes in regular expressions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+
+ Literal(node) {
+ const token = sourceCode.getFirstToken(node);
+
+ if (token.type === "RegularExpression" && !regex.test(token.value)) {
+ context.report({ node, message: "Empty class." });
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-empty-function.js b/tools/node_modules/eslint/lib/rules/no-empty-function.js
new file mode 100644
index 0000000000..38c915c33f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-empty-function.js
@@ -0,0 +1,160 @@
+/**
+ * @fileoverview Rule to disallow empty functions.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const ALLOW_OPTIONS = Object.freeze([
+ "functions",
+ "arrowFunctions",
+ "generatorFunctions",
+ "methods",
+ "generatorMethods",
+ "getters",
+ "setters",
+ "constructors"
+]);
+
+/**
+ * Gets the kind of a given function node.
+ *
+ * @param {ASTNode} node - A function node to get. This is one of
+ * an ArrowFunctionExpression, a FunctionDeclaration, or a
+ * FunctionExpression.
+ * @returns {string} The kind of the function. This is one of "functions",
+ * "arrowFunctions", "generatorFunctions", "asyncFunctions", "methods",
+ * "generatorMethods", "asyncMethods", "getters", "setters", and
+ * "constructors".
+ */
+function getKind(node) {
+ const parent = node.parent;
+ let kind = "";
+
+ if (node.type === "ArrowFunctionExpression") {
+ return "arrowFunctions";
+ }
+
+ // Detects main kind.
+ if (parent.type === "Property") {
+ if (parent.kind === "get") {
+ return "getters";
+ }
+ if (parent.kind === "set") {
+ return "setters";
+ }
+ kind = parent.method ? "methods" : "functions";
+
+ } else if (parent.type === "MethodDefinition") {
+ if (parent.kind === "get") {
+ return "getters";
+ }
+ if (parent.kind === "set") {
+ return "setters";
+ }
+ if (parent.kind === "constructor") {
+ return "constructors";
+ }
+ kind = "methods";
+
+ } else {
+ kind = "functions";
+ }
+
+ // Detects prefix.
+ let prefix = "";
+
+ if (node.generator) {
+ prefix = "generator";
+ } else if (node.async) {
+ prefix = "async";
+ } else {
+ return kind;
+ }
+ return prefix + kind[0].toUpperCase() + kind.slice(1);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow empty functions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allow: {
+ type: "array",
+ items: { enum: ALLOW_OPTIONS },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const allowed = options.allow || [];
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports a given function node if the node matches the following patterns.
+ *
+ * - Not allowed by options.
+ * - The body is empty.
+ * - The body doesn't have any comments.
+ *
+ * @param {ASTNode} node - A function node to report. This is one of
+ * an ArrowFunctionExpression, a FunctionDeclaration, or a
+ * FunctionExpression.
+ * @returns {void}
+ */
+ function reportIfEmpty(node) {
+ const kind = getKind(node);
+ const name = astUtils.getFunctionNameWithKind(node);
+ const innerComments = sourceCode.getTokens(node.body, {
+ includeComments: true,
+ filter: astUtils.isCommentToken
+ });
+
+ if (allowed.indexOf(kind) === -1 &&
+ node.body.type === "BlockStatement" &&
+ node.body.body.length === 0 &&
+ innerComments.length === 0
+ ) {
+ context.report({
+ node,
+ loc: node.body.loc.start,
+ message: "Unexpected empty {{name}}.",
+ data: { name }
+ });
+ }
+ }
+
+ return {
+ ArrowFunctionExpression: reportIfEmpty,
+ FunctionDeclaration: reportIfEmpty,
+ FunctionExpression: reportIfEmpty
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-empty-pattern.js b/tools/node_modules/eslint/lib/rules/no-empty-pattern.js
new file mode 100644
index 0000000000..11f50b5414
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-empty-pattern.js
@@ -0,0 +1,36 @@
+/**
+ * @fileoverview Rule to disallow an empty pattern
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow empty destructuring patterns",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ return {
+ ObjectPattern(node) {
+ if (node.properties.length === 0) {
+ context.report({ node, message: "Unexpected empty object pattern." });
+ }
+ },
+ ArrayPattern(node) {
+ if (node.elements.length === 0) {
+ context.report({ node, message: "Unexpected empty array pattern." });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-empty.js b/tools/node_modules/eslint/lib/rules/no-empty.js
new file mode 100644
index 0000000000..b71b8582a3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-empty.js
@@ -0,0 +1,78 @@
+/**
+ * @fileoverview Rule to flag use of an empty block statement
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow empty block statements",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowEmptyCatch: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {},
+ allowEmptyCatch = options.allowEmptyCatch || false;
+
+ const sourceCode = context.getSourceCode();
+
+ return {
+ BlockStatement(node) {
+
+ // if the body is not empty, we can just return immediately
+ if (node.body.length !== 0) {
+ return;
+ }
+
+ // a function is generally allowed to be empty
+ if (astUtils.isFunction(node.parent)) {
+ return;
+ }
+
+ if (allowEmptyCatch && node.parent.type === "CatchClause") {
+ return;
+ }
+
+ // any other block is only allowed to be empty, if it contains a comment
+ if (sourceCode.getCommentsInside(node).length > 0) {
+ return;
+ }
+
+ context.report({ node, message: "Empty block statement." });
+ },
+
+ SwitchStatement(node) {
+
+ if (typeof node.cases === "undefined" || node.cases.length === 0) {
+ context.report({ node, message: "Empty switch statement." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-eq-null.js b/tools/node_modules/eslint/lib/rules/no-eq-null.js
new file mode 100644
index 0000000000..7e915a8c72
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-eq-null.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Rule to flag comparisons to null without a type-checking
+ * operator.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `null` comparisons without type-checking operators",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ BinaryExpression(node) {
+ const badOperator = node.operator === "==" || node.operator === "!=";
+
+ if (node.right.type === "Literal" && node.right.raw === "null" && badOperator ||
+ node.left.type === "Literal" && node.left.raw === "null" && badOperator) {
+ context.report({ node, message: "Use ‘===’ to compare with ‘null’." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-eval.js b/tools/node_modules/eslint/lib/rules/no-eval.js
new file mode 100644
index 0000000000..ee5f577f47
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-eval.js
@@ -0,0 +1,308 @@
+/**
+ * @fileoverview Rule to flag use of eval() statement
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const candidatesOfGlobalObject = Object.freeze([
+ "global",
+ "window"
+]);
+
+/**
+ * Checks a given node is a Identifier node of the specified name.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - A name to check.
+ * @returns {boolean} `true` if the node is a Identifier node of the name.
+ */
+function isIdentifier(node, name) {
+ return node.type === "Identifier" && node.name === name;
+}
+
+/**
+ * Checks a given node is a Literal node of the specified string value.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - A name to check.
+ * @returns {boolean} `true` if the node is a Literal node of the name.
+ */
+function isConstant(node, name) {
+ switch (node.type) {
+ case "Literal":
+ return node.value === name;
+
+ case "TemplateLiteral":
+ return (
+ node.expressions.length === 0 &&
+ node.quasis[0].value.cooked === name
+ );
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Checks a given node is a MemberExpression node which has the specified name's
+ * property.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - A name to check.
+ * @returns {boolean} `true` if the node is a MemberExpression node which has
+ * the specified name's property
+ */
+function isMember(node, name) {
+ return (
+ node.type === "MemberExpression" &&
+ (node.computed ? isConstant : isIdentifier)(node.property, name)
+ );
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `eval()`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowIndirect: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const allowIndirect = Boolean(
+ context.options[0] &&
+ context.options[0].allowIndirect
+ );
+ const sourceCode = context.getSourceCode();
+ let funcInfo = null;
+
+ /**
+ * Pushs a variable scope (Program or Function) information to the stack.
+ *
+ * This is used in order to check whether or not `this` binding is a
+ * reference to the global object.
+ *
+ * @param {ASTNode} node - A node of the scope. This is one of Program,
+ * FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression.
+ * @returns {void}
+ */
+ function enterVarScope(node) {
+ const strict = context.getScope().isStrict;
+
+ funcInfo = {
+ upper: funcInfo,
+ node,
+ strict,
+ defaultThis: false,
+ initialized: strict
+ };
+ }
+
+ /**
+ * Pops a variable scope from the stack.
+ *
+ * @returns {void}
+ */
+ function exitVarScope() {
+ funcInfo = funcInfo.upper;
+ }
+
+ /**
+ * Reports a given node.
+ *
+ * `node` is `Identifier` or `MemberExpression`.
+ * The parent of `node` might be `CallExpression`.
+ *
+ * The location of the report is always `eval` `Identifier` (or possibly
+ * `Literal`). The type of the report is `CallExpression` if the parent is
+ * `CallExpression`. Otherwise, it's the given node type.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function report(node) {
+ let locationNode = node;
+ const parent = node.parent;
+
+ if (node.type === "MemberExpression") {
+ locationNode = node.property;
+ }
+ if (parent.type === "CallExpression" && parent.callee === node) {
+ node = parent;
+ }
+
+ context.report({
+ node,
+ loc: locationNode.loc.start,
+ message: "eval can be harmful."
+ });
+ }
+
+ /**
+ * Reports accesses of `eval` via the global object.
+ *
+ * @param {eslint-scope.Scope} globalScope - The global scope.
+ * @returns {void}
+ */
+ function reportAccessingEvalViaGlobalObject(globalScope) {
+ for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
+ const name = candidatesOfGlobalObject[i];
+ const variable = astUtils.getVariableByName(globalScope, name);
+
+ if (!variable) {
+ continue;
+ }
+
+ const references = variable.references;
+
+ for (let j = 0; j < references.length; ++j) {
+ const identifier = references[j].identifier;
+ let node = identifier.parent;
+
+ // To detect code like `window.window.eval`.
+ while (isMember(node, name)) {
+ node = node.parent;
+ }
+
+ // Reports.
+ if (isMember(node, "eval")) {
+ report(node);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reports all accesses of `eval` (excludes direct calls to eval).
+ *
+ * @param {eslint-scope.Scope} globalScope - The global scope.
+ * @returns {void}
+ */
+ function reportAccessingEval(globalScope) {
+ const variable = astUtils.getVariableByName(globalScope, "eval");
+
+ if (!variable) {
+ return;
+ }
+
+ const references = variable.references;
+
+ for (let i = 0; i < references.length; ++i) {
+ const reference = references[i];
+ const id = reference.identifier;
+
+ if (id.name === "eval" && !astUtils.isCallee(id)) {
+
+ // Is accessing to eval (excludes direct calls to eval)
+ report(id);
+ }
+ }
+ }
+
+ if (allowIndirect) {
+
+ // Checks only direct calls to eval. It's simple!
+ return {
+ "CallExpression:exit"(node) {
+ const callee = node.callee;
+
+ if (isIdentifier(callee, "eval")) {
+ report(callee);
+ }
+ }
+ };
+ }
+
+ return {
+ "CallExpression:exit"(node) {
+ const callee = node.callee;
+
+ if (isIdentifier(callee, "eval")) {
+ report(callee);
+ }
+ },
+
+ Program(node) {
+ const scope = context.getScope(),
+ features = context.parserOptions.ecmaFeatures || {},
+ strict =
+ scope.isStrict ||
+ node.sourceType === "module" ||
+ (features.globalReturn && scope.childScopes[0].isStrict);
+
+ funcInfo = {
+ upper: null,
+ node,
+ strict,
+ defaultThis: true,
+ initialized: true
+ };
+ },
+
+ "Program:exit"() {
+ const globalScope = context.getScope();
+
+ exitVarScope();
+ reportAccessingEval(globalScope);
+ reportAccessingEvalViaGlobalObject(globalScope);
+ },
+
+ FunctionDeclaration: enterVarScope,
+ "FunctionDeclaration:exit": exitVarScope,
+ FunctionExpression: enterVarScope,
+ "FunctionExpression:exit": exitVarScope,
+ ArrowFunctionExpression: enterVarScope,
+ "ArrowFunctionExpression:exit": exitVarScope,
+
+ ThisExpression(node) {
+ if (!isMember(node.parent, "eval")) {
+ return;
+ }
+
+ /*
+ * `this.eval` is found.
+ * Checks whether or not the value of `this` is the global object.
+ */
+ if (!funcInfo.initialized) {
+ funcInfo.initialized = true;
+ funcInfo.defaultThis = astUtils.isDefaultThisBinding(
+ funcInfo.node,
+ sourceCode
+ );
+ }
+
+ if (!funcInfo.strict && funcInfo.defaultThis) {
+
+ // `this.eval` is possible built-in `eval`.
+ report(node.parent);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-ex-assign.js b/tools/node_modules/eslint/lib/rules/no-ex-assign.js
new file mode 100644
index 0000000000..20869d5cd1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-ex-assign.js
@@ -0,0 +1,45 @@
+/**
+ * @fileoverview Rule to flag assignment of the exception parameter
+ * @author Stephen Murray <spmurrayzzz>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow reassigning exceptions in `catch` clauses",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Finds and reports references that are non initializer and writable.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ astUtils.getModifyingReferences(variable.references).forEach(reference => {
+ context.report({ node: reference.identifier, message: "Do not assign to the exception parameter." });
+ });
+ }
+
+ return {
+ CatchClause(node) {
+ context.getDeclaredVariables(node).forEach(checkVariable);
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-extend-native.js b/tools/node_modules/eslint/lib/rules/no-extend-native.js
new file mode 100644
index 0000000000..c550cf5da5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-extend-native.js
@@ -0,0 +1,174 @@
+/**
+ * @fileoverview Rule to flag adding properties to native object's prototypes.
+ * @author David Nelson
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+const globals = require("globals");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const propertyDefinitionMethods = new Set(["defineProperty", "defineProperties"]);
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow extending native types",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: {
+ type: "string"
+ },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const config = context.options[0] || {};
+ const exceptions = new Set(config.exceptions || []);
+ const modifiedBuiltins = new Set(
+ Object.keys(globals.builtin)
+ .filter(builtin => builtin[0].toUpperCase() === builtin[0])
+ .filter(builtin => !exceptions.has(builtin))
+ );
+
+ /**
+ * Reports a lint error for the given node.
+ * @param {ASTNode} node The node to report.
+ * @param {string} builtin The name of the native builtin being extended.
+ * @returns {void}
+ */
+ function reportNode(node, builtin) {
+ context.report({
+ node,
+ message: "{{builtin}} prototype is read only, properties should not be added.",
+ data: {
+ builtin
+ }
+ });
+ }
+
+ /**
+ * Check to see if the `prototype` property of the given object
+ * identifier node is being accessed.
+ * @param {ASTNode} identifierNode The Identifier representing the object
+ * to check.
+ * @returns {boolean} True if the identifier is the object of a
+ * MemberExpression and its `prototype` property is being accessed,
+ * false otherwise.
+ */
+ function isPrototypePropertyAccessed(identifierNode) {
+ return Boolean(
+ identifierNode &&
+ identifierNode.parent &&
+ identifierNode.parent.type === "MemberExpression" &&
+ identifierNode.parent.object === identifierNode &&
+ astUtils.getStaticPropertyName(identifierNode.parent) === "prototype"
+ );
+ }
+
+ /**
+ * Checks that an identifier is an object of a prototype whose member
+ * is being assigned in an AssignmentExpression.
+ * Example: Object.prototype.foo = "bar"
+ * @param {ASTNode} identifierNode The identifier to check.
+ * @returns {boolean} True if the identifier's prototype is modified.
+ */
+ function isInPrototypePropertyAssignment(identifierNode) {
+ return Boolean(
+ isPrototypePropertyAccessed(identifierNode) &&
+ identifierNode.parent.parent.type === "MemberExpression" &&
+ identifierNode.parent.parent.parent.type === "AssignmentExpression" &&
+ identifierNode.parent.parent.parent.left === identifierNode.parent.parent
+ );
+ }
+
+ /**
+ * Checks that an identifier is an object of a prototype whose member
+ * is being extended via the Object.defineProperty() or
+ * Object.defineProperties() methods.
+ * Example: Object.defineProperty(Array.prototype, "foo", ...)
+ * Example: Object.defineProperties(Array.prototype, ...)
+ * @param {ASTNode} identifierNode The identifier to check.
+ * @returns {boolean} True if the identifier's prototype is modified.
+ */
+ function isInDefinePropertyCall(identifierNode) {
+ return Boolean(
+ isPrototypePropertyAccessed(identifierNode) &&
+ identifierNode.parent.parent.type === "CallExpression" &&
+ identifierNode.parent.parent.arguments[0] === identifierNode.parent &&
+ identifierNode.parent.parent.callee.type === "MemberExpression" &&
+ identifierNode.parent.parent.callee.object.type === "Identifier" &&
+ identifierNode.parent.parent.callee.object.name === "Object" &&
+ identifierNode.parent.parent.callee.property.type === "Identifier" &&
+ propertyDefinitionMethods.has(identifierNode.parent.parent.callee.property.name)
+ );
+ }
+
+ /**
+ * Check to see if object prototype access is part of a prototype
+ * extension. There are three ways a prototype can be extended:
+ * 1. Assignment to prototype property (Object.prototype.foo = 1)
+ * 2. Object.defineProperty()/Object.defineProperties() on a prototype
+ * If prototype extension is detected, report the AssignmentExpression
+ * or CallExpression node.
+ * @param {ASTNode} identifierNode The Identifier representing the object
+ * which prototype is being accessed and possibly extended.
+ * @returns {void}
+ */
+ function checkAndReportPrototypeExtension(identifierNode) {
+ if (isInPrototypePropertyAssignment(identifierNode)) {
+
+ // Identifier --> MemberExpression --> MemberExpression --> AssignmentExpression
+ reportNode(identifierNode.parent.parent.parent, identifierNode.name);
+ } else if (isInDefinePropertyCall(identifierNode)) {
+
+ // Identifier --> MemberExpression --> CallExpression
+ reportNode(identifierNode.parent.parent, identifierNode.name);
+ }
+ }
+
+ return {
+
+ "Program:exit"() {
+ const globalScope = context.getScope();
+
+ modifiedBuiltins.forEach(builtin => {
+ const builtinVar = globalScope.set.get(builtin);
+
+ if (builtinVar && builtinVar.references) {
+ builtinVar.references
+ .map(ref => ref.identifier)
+ .forEach(checkAndReportPrototypeExtension);
+ }
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-bind.js b/tools/node_modules/eslint/lib/rules/no-extra-bind.js
new file mode 100644
index 0000000000..2d22eff245
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-extra-bind.js
@@ -0,0 +1,145 @@
+/**
+ * @fileoverview Rule to flag unnecessary bind calls
+ * @author Bence Dányi <bence@danyi.me>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary calls to `.bind()`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ let scopeInfo = null;
+
+ /**
+ * Reports a given function node.
+ *
+ * @param {ASTNode} node - A node to report. This is a FunctionExpression or
+ * an ArrowFunctionExpression.
+ * @returns {void}
+ */
+ function report(node) {
+ context.report({
+ node: node.parent.parent,
+ message: "The function binding is unnecessary.",
+ loc: node.parent.property.loc.start,
+ fix(fixer) {
+ const firstTokenToRemove = context.getSourceCode()
+ .getFirstTokenBetween(node.parent.object, node.parent.property, astUtils.isNotClosingParenToken);
+
+ return fixer.removeRange([firstTokenToRemove.range[0], node.parent.parent.range[1]]);
+ }
+ });
+ }
+
+ /**
+ * Checks whether or not a given function node is the callee of `.bind()`
+ * method.
+ *
+ * e.g. `(function() {}.bind(foo))`
+ *
+ * @param {ASTNode} node - A node to report. This is a FunctionExpression or
+ * an ArrowFunctionExpression.
+ * @returns {boolean} `true` if the node is the callee of `.bind()` method.
+ */
+ function isCalleeOfBindMethod(node) {
+ const parent = node.parent;
+ const grandparent = parent.parent;
+
+ return (
+ grandparent &&
+ grandparent.type === "CallExpression" &&
+ grandparent.callee === parent &&
+ grandparent.arguments.length === 1 &&
+ parent.type === "MemberExpression" &&
+ parent.object === node &&
+ astUtils.getStaticPropertyName(parent) === "bind"
+ );
+ }
+
+ /**
+ * Adds a scope information object to the stack.
+ *
+ * @param {ASTNode} node - A node to add. This node is a FunctionExpression
+ * or a FunctionDeclaration node.
+ * @returns {void}
+ */
+ function enterFunction(node) {
+ scopeInfo = {
+ isBound: isCalleeOfBindMethod(node),
+ thisFound: false,
+ upper: scopeInfo
+ };
+ }
+
+ /**
+ * Removes the scope information object from the top of the stack.
+ * At the same time, this reports the function node if the function has
+ * `.bind()` and the `this` keywords found.
+ *
+ * @param {ASTNode} node - A node to remove. This node is a
+ * FunctionExpression or a FunctionDeclaration node.
+ * @returns {void}
+ */
+ function exitFunction(node) {
+ if (scopeInfo.isBound && !scopeInfo.thisFound) {
+ report(node);
+ }
+
+ scopeInfo = scopeInfo.upper;
+ }
+
+ /**
+ * Reports a given arrow function if the function is callee of `.bind()`
+ * method.
+ *
+ * @param {ASTNode} node - A node to report. This node is an
+ * ArrowFunctionExpression.
+ * @returns {void}
+ */
+ function exitArrowFunction(node) {
+ if (isCalleeOfBindMethod(node)) {
+ report(node);
+ }
+ }
+
+ /**
+ * Set the mark as the `this` keyword was found in this scope.
+ *
+ * @returns {void}
+ */
+ function markAsThisFound() {
+ if (scopeInfo) {
+ scopeInfo.thisFound = true;
+ }
+ }
+
+ return {
+ "ArrowFunctionExpression:exit": exitArrowFunction,
+ FunctionDeclaration: enterFunction,
+ "FunctionDeclaration:exit": exitFunction,
+ FunctionExpression: enterFunction,
+ "FunctionExpression:exit": exitFunction,
+ ThisExpression: markAsThisFound
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js b/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js
new file mode 100644
index 0000000000..47ca7e22fe
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js
@@ -0,0 +1,122 @@
+/**
+ * @fileoverview Rule to flag unnecessary double negation in Boolean contexts
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary boolean casts",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ // Node types which have a test which will coerce values to booleans.
+ const BOOLEAN_NODE_TYPES = [
+ "IfStatement",
+ "DoWhileStatement",
+ "WhileStatement",
+ "ConditionalExpression",
+ "ForStatement"
+ ];
+
+ /**
+ * Check if a node is in a context where its value would be coerced to a boolean at runtime.
+ *
+ * @param {Object} node The node
+ * @param {Object} parent Its parent
+ * @returns {boolean} If it is in a boolean context
+ */
+ function isInBooleanContext(node, parent) {
+ return (
+ (BOOLEAN_NODE_TYPES.indexOf(parent.type) !== -1 &&
+ node === parent.test) ||
+
+ // !<bool>
+ (parent.type === "UnaryExpression" &&
+ parent.operator === "!")
+ );
+ }
+
+
+ return {
+ UnaryExpression(node) {
+ const ancestors = context.getAncestors(),
+ parent = ancestors.pop(),
+ grandparent = ancestors.pop();
+
+ // Exit early if it's guaranteed not to match
+ if (node.operator !== "!" ||
+ parent.type !== "UnaryExpression" ||
+ parent.operator !== "!") {
+ return;
+ }
+
+ if (isInBooleanContext(parent, grandparent) ||
+
+ // Boolean(<bool>) and new Boolean(<bool>)
+ ((grandparent.type === "CallExpression" || grandparent.type === "NewExpression") &&
+ grandparent.callee.type === "Identifier" &&
+ grandparent.callee.name === "Boolean")
+ ) {
+ context.report({
+ node,
+ message: "Redundant double negation.",
+ fix: fixer => fixer.replaceText(parent, sourceCode.getText(node.argument))
+ });
+ }
+ },
+ CallExpression(node) {
+ const parent = node.parent;
+
+ if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
+ return;
+ }
+
+ if (isInBooleanContext(node, parent)) {
+ context.report({
+ node,
+ message: "Redundant Boolean call.",
+ fix: fixer => {
+ if (!node.arguments.length) {
+ return fixer.replaceText(parent, "true");
+ }
+
+ if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement") {
+ return null;
+ }
+
+ const argument = node.arguments[0];
+
+ if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) {
+ return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
+ }
+ return fixer.replaceText(node, sourceCode.getText(argument));
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-label.js b/tools/node_modules/eslint/lib/rules/no-extra-label.js
new file mode 100644
index 0000000000..b89267de93
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-extra-label.js
@@ -0,0 +1,140 @@
+/**
+ * @fileoverview Rule to disallow unnecessary labels
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary labels",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ let scopeInfo = null;
+
+ /**
+ * Creates a new scope with a breakable statement.
+ *
+ * @param {ASTNode} node - A node to create. This is a BreakableStatement.
+ * @returns {void}
+ */
+ function enterBreakableStatement(node) {
+ scopeInfo = {
+ label: node.parent.type === "LabeledStatement" ? node.parent.label : null,
+ breakable: true,
+ upper: scopeInfo
+ };
+ }
+
+ /**
+ * Removes the top scope of the stack.
+ *
+ * @returns {void}
+ */
+ function exitBreakableStatement() {
+ scopeInfo = scopeInfo.upper;
+ }
+
+ /**
+ * Creates a new scope with a labeled statement.
+ *
+ * This ignores it if the body is a breakable statement.
+ * In this case it's handled in the `enterBreakableStatement` function.
+ *
+ * @param {ASTNode} node - A node to create. This is a LabeledStatement.
+ * @returns {void}
+ */
+ function enterLabeledStatement(node) {
+ if (!astUtils.isBreakableStatement(node.body)) {
+ scopeInfo = {
+ label: node.label,
+ breakable: false,
+ upper: scopeInfo
+ };
+ }
+ }
+
+ /**
+ * Removes the top scope of the stack.
+ *
+ * This ignores it if the body is a breakable statement.
+ * In this case it's handled in the `exitBreakableStatement` function.
+ *
+ * @param {ASTNode} node - A node. This is a LabeledStatement.
+ * @returns {void}
+ */
+ function exitLabeledStatement(node) {
+ if (!astUtils.isBreakableStatement(node.body)) {
+ scopeInfo = scopeInfo.upper;
+ }
+ }
+
+ /**
+ * Reports a given control node if it's unnecessary.
+ *
+ * @param {ASTNode} node - A node. This is a BreakStatement or a
+ * ContinueStatement.
+ * @returns {void}
+ */
+ function reportIfUnnecessary(node) {
+ if (!node.label) {
+ return;
+ }
+
+ const labelNode = node.label;
+
+ for (let info = scopeInfo; info !== null; info = info.upper) {
+ if (info.breakable || info.label && info.label.name === labelNode.name) {
+ if (info.breakable && info.label && info.label.name === labelNode.name) {
+ context.report({
+ node: labelNode,
+ message: "This label '{{name}}' is unnecessary.",
+ data: labelNode,
+ fix: fixer => fixer.removeRange([sourceCode.getFirstToken(node).range[1], labelNode.range[1]])
+ });
+ }
+ return;
+ }
+ }
+ }
+
+ return {
+ WhileStatement: enterBreakableStatement,
+ "WhileStatement:exit": exitBreakableStatement,
+ DoWhileStatement: enterBreakableStatement,
+ "DoWhileStatement:exit": exitBreakableStatement,
+ ForStatement: enterBreakableStatement,
+ "ForStatement:exit": exitBreakableStatement,
+ ForInStatement: enterBreakableStatement,
+ "ForInStatement:exit": exitBreakableStatement,
+ ForOfStatement: enterBreakableStatement,
+ "ForOfStatement:exit": exitBreakableStatement,
+ SwitchStatement: enterBreakableStatement,
+ "SwitchStatement:exit": exitBreakableStatement,
+ LabeledStatement: enterLabeledStatement,
+ "LabeledStatement:exit": exitLabeledStatement,
+ BreakStatement: reportIfUnnecessary,
+ ContinueStatement: reportIfUnnecessary
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-parens.js b/tools/node_modules/eslint/lib/rules/no-extra-parens.js
new file mode 100644
index 0000000000..d8e0df64a7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-extra-parens.js
@@ -0,0 +1,745 @@
+/**
+ * @fileoverview Disallow parenthesising higher precedence subexpressions.
+ * @author Michael Ficarra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils.js");
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary parentheses",
+ category: "Possible Errors",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["functions"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["all"]
+ },
+ {
+ type: "object",
+ properties: {
+ conditionalAssign: { type: "boolean" },
+ nestedBinaryExpressions: { type: "boolean" },
+ returnAssign: { type: "boolean" },
+ ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
+ enforceForArrowConditionals: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ }
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ const tokensToIgnore = new WeakSet();
+ const isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode);
+ const precedence = astUtils.getPrecedence;
+ const ALL_NODES = context.options[0] !== "functions";
+ const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
+ const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
+ const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
+ const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
+ const IGNORE_ARROW_CONDITIONALS = ALL_NODES && context.options[1] &&
+ context.options[1].enforceForArrowConditionals === false;
+
+ const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
+ const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
+
+ /**
+ * Determines if this rule should be enforced for a node given the current configuration.
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the rule should be enforced for this node.
+ * @private
+ */
+ function ruleApplies(node) {
+ if (node.type === "JSXElement") {
+ const isSingleLine = node.loc.start.line === node.loc.end.line;
+
+ switch (IGNORE_JSX) {
+
+ // Exclude this JSX element from linting
+ case "all":
+ return false;
+
+ // Exclude this JSX element if it is multi-line element
+ case "multi-line":
+ return isSingleLine;
+
+ // Exclude this JSX element if it is single-line element
+ case "single-line":
+ return !isSingleLine;
+
+ // Nothing special to be done for JSX elements
+ case "none":
+ break;
+
+ // no default
+ }
+ }
+
+ return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
+ }
+
+ /**
+ * Determines if a node is surrounded by parentheses twice.
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the node is doubly parenthesised.
+ * @private
+ */
+ function isParenthesisedTwice(node) {
+ const previousToken = sourceCode.getTokenBefore(node, 1),
+ nextToken = sourceCode.getTokenAfter(node, 1);
+
+ return isParenthesised(node) && previousToken && nextToken &&
+ astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
+ astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
+ }
+
+ /**
+ * Determines if a node is surrounded by (potentially) invalid parentheses.
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the node is incorrectly parenthesised.
+ * @private
+ */
+ function hasExcessParens(node) {
+ return ruleApplies(node) && isParenthesised(node);
+ }
+
+ /**
+ * Determines if a node that is expected to be parenthesised is surrounded by
+ * (potentially) invalid extra parentheses.
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the node is has an unexpected extra pair of parentheses.
+ * @private
+ */
+ function hasDoubleExcessParens(node) {
+ return ruleApplies(node) && isParenthesisedTwice(node);
+ }
+
+ /**
+ * Determines if a node test expression is allowed to have a parenthesised assignment
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the assignment can be parenthesised.
+ * @private
+ */
+ function isCondAssignException(node) {
+ return EXCEPT_COND_ASSIGN && node.test.type === "AssignmentExpression";
+ }
+
+ /**
+ * Determines if a node is in a return statement
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the node is in a return statement.
+ * @private
+ */
+ function isInReturnStatement(node) {
+ while (node) {
+ if (node.type === "ReturnStatement" ||
+ (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement")) {
+ return true;
+ }
+ node = node.parent;
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if a constructor function is newed-up with parens
+ * @param {ASTNode} newExpression - The NewExpression node to be checked.
+ * @returns {boolean} True if the constructor is called with parens.
+ * @private
+ */
+ function isNewExpressionWithParens(newExpression) {
+ const lastToken = sourceCode.getLastToken(newExpression);
+ const penultimateToken = sourceCode.getTokenBefore(lastToken);
+
+ return newExpression.arguments.length > 0 || astUtils.isOpeningParenToken(penultimateToken) && astUtils.isClosingParenToken(lastToken);
+ }
+
+ /**
+ * Determines if a node is or contains an assignment expression
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the node is or contains an assignment expression.
+ * @private
+ */
+ function containsAssignment(node) {
+ if (node.type === "AssignmentExpression") {
+ return true;
+ }
+ if (node.type === "ConditionalExpression" &&
+ (node.consequent.type === "AssignmentExpression" || node.alternate.type === "AssignmentExpression")) {
+ return true;
+ }
+ if ((node.left && node.left.type === "AssignmentExpression") ||
+ (node.right && node.right.type === "AssignmentExpression")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if a node is contained by or is itself a return statement and is allowed to have a parenthesised assignment
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the assignment can be parenthesised.
+ * @private
+ */
+ function isReturnAssignException(node) {
+ if (!EXCEPT_RETURN_ASSIGN || !isInReturnStatement(node)) {
+ return false;
+ }
+
+ if (node.type === "ReturnStatement") {
+ return node.argument && containsAssignment(node.argument);
+ }
+ if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
+ return containsAssignment(node.body);
+ }
+ return containsAssignment(node);
+
+ }
+
+ /**
+ * Determines if a node following a [no LineTerminator here] restriction is
+ * surrounded by (potentially) invalid extra parentheses.
+ * @param {Token} token - The token preceding the [no LineTerminator here] restriction.
+ * @param {ASTNode} node - The node to be checked.
+ * @returns {boolean} True if the node is incorrectly parenthesised.
+ * @private
+ */
+ function hasExcessParensNoLineTerminator(token, node) {
+ if (token.loc.end.line === node.loc.start.line) {
+ return hasExcessParens(node);
+ }
+
+ return hasDoubleExcessParens(node);
+ }
+
+ /**
+ * Determines whether a node should be preceded by an additional space when removing parens
+ * @param {ASTNode} node node to evaluate; must be surrounded by parentheses
+ * @returns {boolean} `true` if a space should be inserted before the node
+ * @private
+ */
+ function requiresLeadingSpace(node) {
+ const leftParenToken = sourceCode.getTokenBefore(node);
+ const tokenBeforeLeftParen = sourceCode.getTokenBefore(node, 1);
+ const firstToken = sourceCode.getFirstToken(node);
+
+ return tokenBeforeLeftParen &&
+ tokenBeforeLeftParen.range[1] === leftParenToken.range[0] &&
+ leftParenToken.range[1] === firstToken.range[0] &&
+ !astUtils.canTokensBeAdjacent(tokenBeforeLeftParen, firstToken);
+ }
+
+ /**
+ * Determines whether a node should be followed by an additional space when removing parens
+ * @param {ASTNode} node node to evaluate; must be surrounded by parentheses
+ * @returns {boolean} `true` if a space should be inserted after the node
+ * @private
+ */
+ function requiresTrailingSpace(node) {
+ const nextTwoTokens = sourceCode.getTokensAfter(node, { count: 2 });
+ const rightParenToken = nextTwoTokens[0];
+ const tokenAfterRightParen = nextTwoTokens[1];
+ const tokenBeforeRightParen = sourceCode.getLastToken(node);
+
+ return rightParenToken && tokenAfterRightParen &&
+ !sourceCode.isSpaceBetweenTokens(rightParenToken, tokenAfterRightParen) &&
+ !astUtils.canTokensBeAdjacent(tokenBeforeRightParen, tokenAfterRightParen);
+ }
+
+ /**
+ * Determines if a given expression node is an IIFE
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} `true` if the given node is an IIFE
+ */
+ function isIIFE(node) {
+ return node.type === "CallExpression" && node.callee.type === "FunctionExpression";
+ }
+
+ /**
+ * Report the node
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function report(node) {
+ const leftParenToken = sourceCode.getTokenBefore(node);
+ const rightParenToken = sourceCode.getTokenAfter(node);
+
+ if (!isParenthesisedTwice(node)) {
+ if (tokensToIgnore.has(sourceCode.getFirstToken(node))) {
+ return;
+ }
+
+ if (isIIFE(node) && !isParenthesised(node.callee)) {
+ return;
+ }
+ }
+
+ context.report({
+ node,
+ loc: leftParenToken.loc.start,
+ message: "Gratuitous parentheses around expression.",
+ fix(fixer) {
+ const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
+
+ return fixer.replaceTextRange([
+ leftParenToken.range[0],
+ rightParenToken.range[1]
+ ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
+ }
+ });
+ }
+
+ /**
+ * Evaluate Unary update
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkUnaryUpdate(node) {
+ if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") {
+ return;
+ }
+
+ if (hasExcessParens(node.argument) && precedence(node.argument) >= precedence(node)) {
+ report(node.argument);
+ }
+ }
+
+ /**
+ * Check if a member expression contains a call expression
+ * @param {ASTNode} node MemberExpression node to evaluate
+ * @returns {boolean} true if found, false if not
+ */
+ function doesMemberExpressionContainCallExpression(node) {
+ let currentNode = node.object;
+ let currentNodeType = node.object.type;
+
+ while (currentNodeType === "MemberExpression") {
+ currentNode = currentNode.object;
+ currentNodeType = currentNode.type;
+ }
+
+ return currentNodeType === "CallExpression";
+ }
+
+ /**
+ * Evaluate a new call
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkCallNew(node) {
+ const callee = node.callee;
+
+ if (hasExcessParens(callee) && precedence(callee) >= precedence(node)) {
+ const hasNewParensException = callee.type === "NewExpression" && !isNewExpressionWithParens(callee);
+
+ if (
+ hasDoubleExcessParens(callee) ||
+ !isIIFE(node) && !hasNewParensException && !(
+
+ /*
+ * Allow extra parens around a new expression if
+ * there are intervening parentheses.
+ */
+ callee.type === "MemberExpression" &&
+ doesMemberExpressionContainCallExpression(callee)
+ )
+ ) {
+ report(node.callee);
+ }
+ }
+ if (node.arguments.length === 1) {
+ if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+ report(node.arguments[0]);
+ }
+ } else {
+ node.arguments
+ .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
+ .forEach(report);
+ }
+ }
+
+ /**
+ * Evaluate binary logicals
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkBinaryLogical(node) {
+ const prec = precedence(node);
+ const leftPrecedence = precedence(node.left);
+ const rightPrecedence = precedence(node.right);
+ const isExponentiation = node.operator === "**";
+ const shouldSkipLeft = (NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression")) ||
+ node.left.type === "UnaryExpression" && isExponentiation;
+ const shouldSkipRight = NESTED_BINARY && (node.right.type === "BinaryExpression" || node.right.type === "LogicalExpression");
+
+ if (!shouldSkipLeft && hasExcessParens(node.left) && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))) {
+ report(node.left);
+ }
+ if (!shouldSkipRight && hasExcessParens(node.right) && (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation))) {
+ report(node.right);
+ }
+ }
+
+ /**
+ * Check the parentheses around the super class of the given class definition.
+ * @param {ASTNode} node The node of class declarations to check.
+ * @returns {void}
+ */
+ function checkClass(node) {
+ if (!node.superClass) {
+ return;
+ }
+
+ /*
+ * If `node.superClass` is a LeftHandSideExpression, parentheses are extra.
+ * Otherwise, parentheses are needed.
+ */
+ const hasExtraParens = precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR
+ ? hasExcessParens(node.superClass)
+ : hasDoubleExcessParens(node.superClass);
+
+ if (hasExtraParens) {
+ report(node.superClass);
+ }
+ }
+
+ /**
+ * Check the parentheses around the argument of the given spread operator.
+ * @param {ASTNode} node The node of spread elements/properties to check.
+ * @returns {void}
+ */
+ function checkSpreadOperator(node) {
+ const hasExtraParens = precedence(node.argument) >= PRECEDENCE_OF_ASSIGNMENT_EXPR
+ ? hasExcessParens(node.argument)
+ : hasDoubleExcessParens(node.argument);
+
+ if (hasExtraParens) {
+ report(node.argument);
+ }
+ }
+
+ /**
+ * Checks the parentheses for an ExpressionStatement or ExportDefaultDeclaration
+ * @param {ASTNode} node The ExpressionStatement.expression or ExportDefaultDeclaration.declaration node
+ * @returns {void}
+ */
+ function checkExpressionOrExportStatement(node) {
+ const firstToken = isParenthesised(node) ? sourceCode.getTokenBefore(node) : sourceCode.getFirstToken(node);
+ const secondToken = sourceCode.getTokenAfter(firstToken, astUtils.isNotOpeningParenToken);
+ const thirdToken = secondToken ? sourceCode.getTokenAfter(secondToken) : null;
+
+ if (
+ astUtils.isOpeningParenToken(firstToken) &&
+ (
+ astUtils.isOpeningBraceToken(secondToken) ||
+ secondToken.type === "Keyword" && (
+ secondToken.value === "function" ||
+ secondToken.value === "class" ||
+ secondToken.value === "let" && astUtils.isOpeningBracketToken(sourceCode.getTokenAfter(secondToken, astUtils.isNotClosingParenToken))
+ ) ||
+ secondToken && secondToken.type === "Identifier" && secondToken.value === "async" && thirdToken && thirdToken.type === "Keyword" && thirdToken.value === "function"
+ )
+ ) {
+ tokensToIgnore.add(secondToken);
+ }
+
+ if (hasExcessParens(node)) {
+ report(node);
+ }
+ }
+
+ return {
+ ArrayExpression(node) {
+ node.elements
+ .filter(e => e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
+ .forEach(report);
+ },
+
+ ArrowFunctionExpression(node) {
+ if (isReturnAssignException(node)) {
+ return;
+ }
+
+ if (node.body.type === "ConditionalExpression" &&
+ IGNORE_ARROW_CONDITIONALS &&
+ !isParenthesisedTwice(node.body)
+ ) {
+ return;
+ }
+
+ if (node.body.type !== "BlockStatement") {
+ const firstBodyToken = sourceCode.getFirstToken(node.body, astUtils.isNotOpeningParenToken);
+ const tokenBeforeFirst = sourceCode.getTokenBefore(firstBodyToken);
+
+ if (astUtils.isOpeningParenToken(tokenBeforeFirst) && astUtils.isOpeningBraceToken(firstBodyToken)) {
+ tokensToIgnore.add(firstBodyToken);
+ }
+ if (hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+ report(node.body);
+ }
+ }
+ },
+
+ AssignmentExpression(node) {
+ if (isReturnAssignException(node)) {
+ return;
+ }
+
+ if (hasExcessParens(node.right) && precedence(node.right) >= precedence(node)) {
+ report(node.right);
+ }
+ },
+
+ BinaryExpression: checkBinaryLogical,
+ CallExpression: checkCallNew,
+
+ ConditionalExpression(node) {
+ if (isReturnAssignException(node)) {
+ return;
+ }
+
+ if (hasExcessParens(node.test) && precedence(node.test) >= precedence({ type: "LogicalExpression", operator: "||" })) {
+ report(node.test);
+ }
+
+ if (hasExcessParens(node.consequent) && precedence(node.consequent) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+ report(node.consequent);
+ }
+
+ if (hasExcessParens(node.alternate) && precedence(node.alternate) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+ report(node.alternate);
+ }
+ },
+
+ DoWhileStatement(node) {
+ if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
+ report(node.test);
+ }
+ },
+
+ ExportDefaultDeclaration: node => checkExpressionOrExportStatement(node.declaration),
+ ExpressionStatement: node => checkExpressionOrExportStatement(node.expression),
+
+ "ForInStatement, ForOfStatement"(node) {
+ if (node.left.type !== "VariableDeclarator") {
+ const firstLeftToken = sourceCode.getFirstToken(node.left, astUtils.isNotOpeningParenToken);
+
+ if (
+ firstLeftToken.value === "let" && (
+
+ /*
+ * If `let` is the only thing on the left side of the loop, it's the loop variable: `for ((let) of foo);`
+ * Removing it will cause a syntax error, because it will be parsed as the start of a VariableDeclarator.
+ */
+ firstLeftToken.range[1] === node.left.range[1] ||
+
+ /*
+ * If `let` is followed by a `[` token, it's a property access on the `let` value: `for ((let[foo]) of bar);`
+ * Removing it will cause the property access to be parsed as a destructuring declaration of `foo` instead.
+ */
+ astUtils.isOpeningBracketToken(
+ sourceCode.getTokenAfter(firstLeftToken, astUtils.isNotClosingParenToken)
+ )
+ )
+ ) {
+ tokensToIgnore.add(firstLeftToken);
+ }
+ }
+ if (hasExcessParens(node.right)) {
+ report(node.right);
+ }
+ if (hasExcessParens(node.left)) {
+ report(node.left);
+ }
+ },
+
+ ForStatement(node) {
+ if (node.init && hasExcessParens(node.init)) {
+ report(node.init);
+ }
+
+ if (node.test && hasExcessParens(node.test) && !isCondAssignException(node)) {
+ report(node.test);
+ }
+
+ if (node.update && hasExcessParens(node.update)) {
+ report(node.update);
+ }
+ },
+
+ IfStatement(node) {
+ if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
+ report(node.test);
+ }
+ },
+
+ LogicalExpression: checkBinaryLogical,
+
+ MemberExpression(node) {
+ const nodeObjHasExcessParens = hasExcessParens(node.object);
+
+ if (
+ nodeObjHasExcessParens &&
+ precedence(node.object) >= precedence(node) &&
+ (
+ node.computed ||
+ !(
+ astUtils.isDecimalInteger(node.object) ||
+
+ // RegExp literal is allowed to have parens (#1589)
+ (node.object.type === "Literal" && node.object.regex)
+ )
+ )
+ ) {
+ report(node.object);
+ }
+
+ if (nodeObjHasExcessParens &&
+ node.object.type === "CallExpression" &&
+ node.parent.type !== "NewExpression") {
+ report(node.object);
+ }
+
+ if (node.computed && hasExcessParens(node.property)) {
+ report(node.property);
+ }
+ },
+
+ NewExpression: checkCallNew,
+
+ ObjectExpression(node) {
+ node.properties
+ .filter(property => {
+ const value = property.value;
+
+ return value && hasExcessParens(value) && precedence(value) >= PRECEDENCE_OF_ASSIGNMENT_EXPR;
+ }).forEach(property => report(property.value));
+ },
+
+ ReturnStatement(node) {
+ const returnToken = sourceCode.getFirstToken(node);
+
+ if (isReturnAssignException(node)) {
+ return;
+ }
+
+ if (node.argument &&
+ hasExcessParensNoLineTerminator(returnToken, node.argument) &&
+
+ // RegExp literal is allowed to have parens (#1589)
+ !(node.argument.type === "Literal" && node.argument.regex)) {
+ report(node.argument);
+ }
+ },
+
+ SequenceExpression(node) {
+ node.expressions
+ .filter(e => hasExcessParens(e) && precedence(e) >= precedence(node))
+ .forEach(report);
+ },
+
+ SwitchCase(node) {
+ if (node.test && hasExcessParens(node.test)) {
+ report(node.test);
+ }
+ },
+
+ SwitchStatement(node) {
+ if (hasDoubleExcessParens(node.discriminant)) {
+ report(node.discriminant);
+ }
+ },
+
+ ThrowStatement(node) {
+ const throwToken = sourceCode.getFirstToken(node);
+
+ if (hasExcessParensNoLineTerminator(throwToken, node.argument)) {
+ report(node.argument);
+ }
+ },
+
+ UnaryExpression: checkUnaryUpdate,
+ UpdateExpression: checkUnaryUpdate,
+ AwaitExpression: checkUnaryUpdate,
+
+ VariableDeclarator(node) {
+ if (node.init && hasExcessParens(node.init) &&
+ precedence(node.init) >= PRECEDENCE_OF_ASSIGNMENT_EXPR &&
+
+ // RegExp literal is allowed to have parens (#1589)
+ !(node.init.type === "Literal" && node.init.regex)) {
+ report(node.init);
+ }
+ },
+
+ WhileStatement(node) {
+ if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
+ report(node.test);
+ }
+ },
+
+ WithStatement(node) {
+ if (hasDoubleExcessParens(node.object)) {
+ report(node.object);
+ }
+ },
+
+ YieldExpression(node) {
+ if (node.argument) {
+ const yieldToken = sourceCode.getFirstToken(node);
+
+ if ((precedence(node.argument) >= precedence(node) &&
+ hasExcessParensNoLineTerminator(yieldToken, node.argument)) ||
+ hasDoubleExcessParens(node.argument)) {
+ report(node.argument);
+ }
+ }
+ },
+
+ ClassDeclaration: checkClass,
+ ClassExpression: checkClass,
+
+ SpreadElement: checkSpreadOperator,
+ SpreadProperty: checkSpreadOperator,
+ ExperimentalSpreadProperty: checkSpreadOperator
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-semi.js b/tools/node_modules/eslint/lib/rules/no-extra-semi.js
new file mode 100644
index 0000000000..acd312b32b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-extra-semi.js
@@ -0,0 +1,120 @@
+/**
+ * @fileoverview Rule to flag use of unnecessary semicolons
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const FixTracker = require("../util/fix-tracker");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary semicolons",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ fixable: "code",
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports an unnecessary semicolon error.
+ * @param {Node|Token} nodeOrToken - A node or a token to be reported.
+ * @returns {void}
+ */
+ function report(nodeOrToken) {
+ context.report({
+ node: nodeOrToken,
+ message: "Unnecessary semicolon.",
+ fix(fixer) {
+
+ /*
+ * Expand the replacement range to include the surrounding
+ * tokens to avoid conflicting with semi.
+ * https://github.com/eslint/eslint/issues/7928
+ */
+ return new FixTracker(fixer, context.getSourceCode())
+ .retainSurroundingTokens(nodeOrToken)
+ .remove(nodeOrToken);
+ }
+ });
+ }
+
+ /**
+ * Checks for a part of a class body.
+ * This checks tokens from a specified token to a next MethodDefinition or the end of class body.
+ *
+ * @param {Token} firstToken - The first token to check.
+ * @returns {void}
+ */
+ function checkForPartOfClassBody(firstToken) {
+ for (let token = firstToken;
+ token.type === "Punctuator" && !astUtils.isClosingBraceToken(token);
+ token = sourceCode.getTokenAfter(token)
+ ) {
+ if (astUtils.isSemicolonToken(token)) {
+ report(token);
+ }
+ }
+ }
+
+ return {
+
+ /**
+ * Reports this empty statement, except if the parent node is a loop.
+ * @param {Node} node - A EmptyStatement node to be reported.
+ * @returns {void}
+ */
+ EmptyStatement(node) {
+ const parent = node.parent,
+ allowedParentTypes = [
+ "ForStatement",
+ "ForInStatement",
+ "ForOfStatement",
+ "WhileStatement",
+ "DoWhileStatement",
+ "IfStatement",
+ "LabeledStatement",
+ "WithStatement"
+ ];
+
+ if (allowedParentTypes.indexOf(parent.type) === -1) {
+ report(node);
+ }
+ },
+
+ /**
+ * Checks tokens from the head of this class body to the first MethodDefinition or the end of this class body.
+ * @param {Node} node - A ClassBody node to check.
+ * @returns {void}
+ */
+ ClassBody(node) {
+ checkForPartOfClassBody(sourceCode.getFirstToken(node, 1)); // 0 is `{`.
+ },
+
+ /**
+ * Checks tokens from this MethodDefinition to the next MethodDefinition or the end of this class body.
+ * @param {Node} node - A MethodDefinition node of the start point.
+ * @returns {void}
+ */
+ MethodDefinition(node) {
+ checkForPartOfClassBody(sourceCode.getTokenAfter(node));
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-fallthrough.js b/tools/node_modules/eslint/lib/rules/no-fallthrough.js
new file mode 100644
index 0000000000..082e8431d6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-fallthrough.js
@@ -0,0 +1,135 @@
+/**
+ * @fileoverview Rule to flag fall-through cases in switch statements.
+ * @author Matt DuVall <http://mattduvall.com/>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/i;
+
+/**
+ * Checks whether or not a given node has a fallthrough comment.
+ * @param {ASTNode} node - A SwitchCase node to get comments.
+ * @param {RuleContext} context - A rule context which stores comments.
+ * @param {RegExp} fallthroughCommentPattern - A pattern to match comment to.
+ * @returns {boolean} `true` if the node has a valid fallthrough comment.
+ */
+function hasFallthroughComment(node, context, fallthroughCommentPattern) {
+ const sourceCode = context.getSourceCode();
+ const comment = lodash.last(sourceCode.getCommentsBefore(node));
+
+ return Boolean(comment && fallthroughCommentPattern.test(comment.value));
+}
+
+/**
+ * Checks whether or not a given code path segment is reachable.
+ * @param {CodePathSegment} segment - A CodePathSegment to check.
+ * @returns {boolean} `true` if the segment is reachable.
+ */
+function isReachable(segment) {
+ return segment.reachable;
+}
+
+/**
+ * Checks whether a node and a token are separated by blank lines
+ * @param {ASTNode} node - The node to check
+ * @param {Token} token - The token to compare against
+ * @returns {boolean} `true` if there are blank lines between node and token
+ */
+function hasBlankLinesBetween(node, token) {
+ return token.loc.start.line > node.loc.end.line + 1;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow fallthrough of `case` statements",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ commentPattern: {
+ type: "string"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ let currentCodePath = null;
+ const sourceCode = context.getSourceCode();
+
+ /*
+ * We need to use leading comments of the next SwitchCase node because
+ * trailing comments is wrong if semicolons are omitted.
+ */
+ let fallthroughCase = null;
+ let fallthroughCommentPattern = null;
+
+ if (options.commentPattern) {
+ fallthroughCommentPattern = new RegExp(options.commentPattern);
+ } else {
+ fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
+ }
+
+ return {
+ onCodePathStart(codePath) {
+ currentCodePath = codePath;
+ },
+ onCodePathEnd() {
+ currentCodePath = currentCodePath.upper;
+ },
+
+ SwitchCase(node) {
+
+ /*
+ * Checks whether or not there is a fallthrough comment.
+ * And reports the previous fallthrough node if that does not exist.
+ */
+ if (fallthroughCase && !hasFallthroughComment(node, context, fallthroughCommentPattern)) {
+ context.report({
+ message: "Expected a 'break' statement before '{{type}}'.",
+ data: { type: node.test ? "case" : "default" },
+ node
+ });
+ }
+ fallthroughCase = null;
+ },
+
+ "SwitchCase:exit"(node) {
+ const nextToken = sourceCode.getTokenAfter(node);
+
+ /*
+ * `reachable` meant fall through because statements preceded by
+ * `break`, `return`, or `throw` are unreachable.
+ * And allows empty cases and the last case.
+ */
+ if (currentCodePath.currentSegments.some(isReachable) &&
+ (node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
+ lodash.last(node.parent.cases) !== node) {
+ fallthroughCase = node;
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-floating-decimal.js b/tools/node_modules/eslint/lib/rules/no-floating-decimal.js
new file mode 100644
index 0000000000..dfba453a49
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-floating-decimal.js
@@ -0,0 +1,64 @@
+/**
+ * @fileoverview Rule to flag use of a leading/trailing decimal point in a numeric literal
+ * @author James Allardice
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow leading or trailing decimal points in numeric literals",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ Literal(node) {
+
+ if (typeof node.value === "number") {
+ if (node.raw.startsWith(".")) {
+ context.report({
+ node,
+ message: "A leading decimal point can be confused with a dot.",
+ fix(fixer) {
+ const tokenBefore = sourceCode.getTokenBefore(node);
+ const needsSpaceBefore = tokenBefore &&
+ tokenBefore.range[1] === node.range[0] &&
+ !astUtils.canTokensBeAdjacent(tokenBefore, `0${node.raw}`);
+
+ return fixer.insertTextBefore(node, needsSpaceBefore ? " 0" : "0");
+ }
+ });
+ }
+ if (node.raw.indexOf(".") === node.raw.length - 1) {
+ context.report({
+ node,
+ message: "A trailing decimal point can be confused with a dot.",
+ fix: fixer => fixer.insertTextAfter(node, "0")
+ });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-func-assign.js b/tools/node_modules/eslint/lib/rules/no-func-assign.js
new file mode 100644
index 0000000000..ea86365b29
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-func-assign.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Rule to flag use of function declaration identifiers as variables.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow reassigning `function` declarations",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Reports a reference if is non initializer and writable.
+ * @param {References} references - Collection of reference to check.
+ * @returns {void}
+ */
+ function checkReference(references) {
+ astUtils.getModifyingReferences(references).forEach(reference => {
+ context.report({ node: reference.identifier, message: "'{{name}}' is a function.", data: { name: reference.identifier.name } });
+ });
+ }
+
+ /**
+ * Finds and reports references that are non initializer and writable.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ if (variable.defs[0].type === "FunctionName") {
+ checkReference(variable.references);
+ }
+ }
+
+ /**
+ * Checks parameters of a given function node.
+ * @param {ASTNode} node - A function node to check.
+ * @returns {void}
+ */
+ function checkForFunction(node) {
+ context.getDeclaredVariables(node).forEach(checkVariable);
+ }
+
+ return {
+ FunctionDeclaration: checkForFunction,
+ FunctionExpression: checkForFunction
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-global-assign.js b/tools/node_modules/eslint/lib/rules/no-global-assign.js
new file mode 100644
index 0000000000..679650cb70
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-global-assign.js
@@ -0,0 +1,85 @@
+/**
+ * @fileoverview Rule to disallow assignments to native objects or read-only global variables
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow assignments to native objects or read-only global variables",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: { type: "string" },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const config = context.options[0];
+ const exceptions = (config && config.exceptions) || [];
+
+ /**
+ * Reports write references.
+ * @param {Reference} reference - A reference to check.
+ * @param {int} index - The index of the reference in the references.
+ * @param {Reference[]} references - The array that the reference belongs to.
+ * @returns {void}
+ */
+ function checkReference(reference, index, references) {
+ const identifier = reference.identifier;
+
+ if (reference.init === false &&
+ reference.isWrite() &&
+
+ /*
+ * Destructuring assignments can have multiple default value,
+ * so possibly there are multiple writeable references for the same identifier.
+ */
+ (index === 0 || references[index - 1].identifier !== identifier)
+ ) {
+ context.report({
+ node: identifier,
+ message: "Read-only global '{{name}}' should not be modified.",
+ data: identifier
+ });
+ }
+ }
+
+ /**
+ * Reports write references if a given variable is read-only builtin.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
+ variable.references.forEach(checkReference);
+ }
+ }
+
+ return {
+ Program() {
+ const globalScope = context.getScope();
+
+ globalScope.variables.forEach(checkVariable);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-implicit-coercion.js b/tools/node_modules/eslint/lib/rules/no-implicit-coercion.js
new file mode 100644
index 0000000000..24e04858f0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-implicit-coercion.js
@@ -0,0 +1,292 @@
+/**
+ * @fileoverview A rule to disallow the type conversions with shorter notations.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const INDEX_OF_PATTERN = /^(?:i|lastI)ndexOf$/;
+const ALLOWABLE_OPERATORS = ["~", "!!", "+", "*"];
+
+/**
+ * Parses and normalizes an option object.
+ * @param {Object} options - An option object to parse.
+ * @returns {Object} The parsed and normalized option object.
+ */
+function parseOptions(options) {
+ options = options || {};
+ return {
+ boolean: "boolean" in options ? Boolean(options.boolean) : true,
+ number: "number" in options ? Boolean(options.number) : true,
+ string: "string" in options ? Boolean(options.string) : true,
+ allow: options.allow || []
+ };
+}
+
+/**
+ * Checks whether or not a node is a double logical nigating.
+ * @param {ASTNode} node - An UnaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a double logical nigating.
+ */
+function isDoubleLogicalNegating(node) {
+ return (
+ node.operator === "!" &&
+ node.argument.type === "UnaryExpression" &&
+ node.argument.operator === "!"
+ );
+}
+
+/**
+ * Checks whether or not a node is a binary negating of `.indexOf()` method calling.
+ * @param {ASTNode} node - An UnaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
+ */
+function isBinaryNegatingOfIndexOf(node) {
+ return (
+ node.operator === "~" &&
+ node.argument.type === "CallExpression" &&
+ node.argument.callee.type === "MemberExpression" &&
+ node.argument.callee.property.type === "Identifier" &&
+ INDEX_OF_PATTERN.test(node.argument.callee.property.name)
+ );
+}
+
+/**
+ * Checks whether or not a node is a multiplying by one.
+ * @param {BinaryExpression} node - A BinaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a multiplying by one.
+ */
+function isMultiplyByOne(node) {
+ return node.operator === "*" && (
+ node.left.type === "Literal" && node.left.value === 1 ||
+ node.right.type === "Literal" && node.right.value === 1
+ );
+}
+
+/**
+ * Checks whether the result of a node is numeric or not
+ * @param {ASTNode} node The node to test
+ * @returns {boolean} true if the node is a number literal or a `Number()`, `parseInt` or `parseFloat` call
+ */
+function isNumeric(node) {
+ return (
+ node.type === "Literal" && typeof node.value === "number" ||
+ node.type === "CallExpression" && (
+ node.callee.name === "Number" ||
+ node.callee.name === "parseInt" ||
+ node.callee.name === "parseFloat"
+ )
+ );
+}
+
+/**
+ * Returns the first non-numeric operand in a BinaryExpression. Designed to be
+ * used from bottom to up since it walks up the BinaryExpression trees using
+ * node.parent to find the result.
+ * @param {BinaryExpression} node The BinaryExpression node to be walked up on
+ * @returns {ASTNode|null} The first non-numeric item in the BinaryExpression tree or null
+ */
+function getNonNumericOperand(node) {
+ const left = node.left,
+ right = node.right;
+
+ if (right.type !== "BinaryExpression" && !isNumeric(right)) {
+ return right;
+ }
+
+ if (left.type !== "BinaryExpression" && !isNumeric(left)) {
+ return left;
+ }
+
+ return null;
+}
+
+/**
+ * Checks whether a node is an empty string literal or not.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} Whether or not the passed in node is an
+ * empty string literal or not.
+ */
+function isEmptyString(node) {
+ return astUtils.isStringLiteral(node) && (node.value === "" || (node.type === "TemplateLiteral" && node.quasis.length === 1 && node.quasis[0].value.cooked === ""));
+}
+
+/**
+ * Checks whether or not a node is a concatenating with an empty string.
+ * @param {ASTNode} node - A BinaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a concatenating with an empty string.
+ */
+function isConcatWithEmptyString(node) {
+ return node.operator === "+" && (
+ (isEmptyString(node.left) && !astUtils.isStringLiteral(node.right)) ||
+ (isEmptyString(node.right) && !astUtils.isStringLiteral(node.left))
+ );
+}
+
+/**
+ * Checks whether or not a node is appended with an empty string.
+ * @param {ASTNode} node - An AssignmentExpression node to check.
+ * @returns {boolean} Whether or not the node is appended with an empty string.
+ */
+function isAppendEmptyString(node) {
+ return node.operator === "+=" && isEmptyString(node.right);
+}
+
+/**
+ * Returns the operand that is not an empty string from a flagged BinaryExpression.
+ * @param {ASTNode} node - The flagged BinaryExpression node to check.
+ * @returns {ASTNode} The operand that is not an empty string from a flagged BinaryExpression.
+ */
+function getNonEmptyOperand(node) {
+ return isEmptyString(node.left) ? node.right : node.left;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow shorthand type conversions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ fixable: "code",
+ schema: [{
+ type: "object",
+ properties: {
+ boolean: {
+ type: "boolean"
+ },
+ number: {
+ type: "boolean"
+ },
+ string: {
+ type: "boolean"
+ },
+ allow: {
+ type: "array",
+ items: {
+ enum: ALLOWABLE_OPERATORS
+ },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+ const options = parseOptions(context.options[0]);
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports an error and autofixes the node
+ * @param {ASTNode} node - An ast node to report the error on.
+ * @param {string} recommendation - The recommended code for the issue
+ * @param {bool} shouldFix - Whether this report should fix the node
+ * @returns {void}
+ */
+ function report(node, recommendation, shouldFix) {
+ shouldFix = typeof shouldFix === "undefined" ? true : shouldFix;
+
+ context.report({
+ node,
+ message: "use `{{recommendation}}` instead.",
+ data: {
+ recommendation
+ },
+ fix(fixer) {
+ if (!shouldFix) {
+ return null;
+ }
+
+ const tokenBefore = sourceCode.getTokenBefore(node);
+
+ if (
+ tokenBefore &&
+ tokenBefore.range[1] === node.range[0] &&
+ !astUtils.canTokensBeAdjacent(tokenBefore, recommendation)
+ ) {
+ return fixer.replaceText(node, ` ${recommendation}`);
+ }
+ return fixer.replaceText(node, recommendation);
+ }
+ });
+ }
+
+ return {
+ UnaryExpression(node) {
+ let operatorAllowed;
+
+ // !!foo
+ operatorAllowed = options.allow.indexOf("!!") >= 0;
+ if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
+ const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
+
+ report(node, recommendation);
+ }
+
+ // ~foo.indexOf(bar)
+ operatorAllowed = options.allow.indexOf("~") >= 0;
+ if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
+ const recommendation = `${sourceCode.getText(node.argument)} !== -1`;
+
+ report(node, recommendation, false);
+ }
+
+ // +foo
+ operatorAllowed = options.allow.indexOf("+") >= 0;
+ if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
+ const recommendation = `Number(${sourceCode.getText(node.argument)})`;
+
+ report(node, recommendation);
+ }
+ },
+
+ // Use `:exit` to prevent double reporting
+ "BinaryExpression:exit"(node) {
+ let operatorAllowed;
+
+ // 1 * foo
+ operatorAllowed = options.allow.indexOf("*") >= 0;
+ const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
+
+ if (nonNumericOperand) {
+ const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
+
+ report(node, recommendation);
+ }
+
+ // "" + foo
+ operatorAllowed = options.allow.indexOf("+") >= 0;
+ if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
+ const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
+
+ report(node, recommendation);
+ }
+ },
+
+ AssignmentExpression(node) {
+
+ // foo += ""
+ const operatorAllowed = options.allow.indexOf("+") >= 0;
+
+ if (!operatorAllowed && options.string && isAppendEmptyString(node)) {
+ const code = sourceCode.getText(getNonEmptyOperand(node));
+ const recommendation = `${code} = String(${code})`;
+
+ report(node, recommendation);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js
new file mode 100644
index 0000000000..f0962cdc7a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js
@@ -0,0 +1,55 @@
+/**
+ * @fileoverview Rule to check for implicit global variables and functions.
+ * @author Joshua Peek
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow variable and `function` declarations in the global scope",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ return {
+ Program() {
+ const scope = context.getScope();
+
+ scope.variables.forEach(variable => {
+ if (variable.writeable) {
+ return;
+ }
+
+ variable.defs.forEach(def => {
+ if (def.type === "FunctionName" || (def.type === "Variable" && def.parent.kind === "var")) {
+ context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." });
+ }
+ });
+ });
+
+ scope.implicit.variables.forEach(variable => {
+ const scopeVariable = scope.set.get(variable.name);
+
+ if (scopeVariable && scopeVariable.writeable) {
+ return;
+ }
+
+ variable.defs.forEach(def => {
+ context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." });
+ });
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-implied-eval.js b/tools/node_modules/eslint/lib/rules/no-implied-eval.js
new file mode 100644
index 0000000000..cfb16dbf73
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-implied-eval.js
@@ -0,0 +1,161 @@
+/**
+ * @fileoverview Rule to flag use of implied eval via setTimeout and setInterval
+ * @author James Allardice
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `eval()`-like methods",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const CALLEE_RE = /^(setTimeout|setInterval|execScript)$/;
+
+ /*
+ * Figures out if we should inspect a given binary expression. Is a stack
+ * of stacks, where the first element in each substack is a CallExpression.
+ */
+ const impliedEvalAncestorsStack = [];
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Get the last element of an array, without modifying arr, like pop(), but non-destructive.
+ * @param {array} arr What to inspect
+ * @returns {*} The last element of arr
+ * @private
+ */
+ function last(arr) {
+ return arr ? arr[arr.length - 1] : null;
+ }
+
+ /**
+ * Checks if the given MemberExpression node is a potentially implied eval identifier on window.
+ * @param {ASTNode} node The MemberExpression node to check.
+ * @returns {boolean} Whether or not the given node is potentially an implied eval.
+ * @private
+ */
+ function isImpliedEvalMemberExpression(node) {
+ const object = node.object,
+ property = node.property,
+ hasImpliedEvalName = CALLEE_RE.test(property.name) || CALLEE_RE.test(property.value);
+
+ return object.name === "window" && hasImpliedEvalName;
+ }
+
+ /**
+ * Determines if a node represents a call to a potentially implied eval.
+ *
+ * This checks the callee name and that there's an argument, but not the type of the argument.
+ *
+ * @param {ASTNode} node The CallExpression to check.
+ * @returns {boolean} True if the node matches, false if not.
+ * @private
+ */
+ function isImpliedEvalCallExpression(node) {
+ const isMemberExpression = (node.callee.type === "MemberExpression"),
+ isIdentifier = (node.callee.type === "Identifier"),
+ isImpliedEvalCallee =
+ (isIdentifier && CALLEE_RE.test(node.callee.name)) ||
+ (isMemberExpression && isImpliedEvalMemberExpression(node.callee));
+
+ return isImpliedEvalCallee && node.arguments.length;
+ }
+
+ /**
+ * Checks that the parent is a direct descendent of an potential implied eval CallExpression, and if the parent is a CallExpression, that we're the first argument.
+ * @param {ASTNode} node The node to inspect the parent of.
+ * @returns {boolean} Was the parent a direct descendent, and is the child therefore potentially part of a dangerous argument?
+ * @private
+ */
+ function hasImpliedEvalParent(node) {
+
+ // make sure our parent is marked
+ return node.parent === last(last(impliedEvalAncestorsStack)) &&
+
+ // if our parent is a CallExpression, make sure we're the first argument
+ (node.parent.type !== "CallExpression" || node === node.parent.arguments[0]);
+ }
+
+ /**
+ * Checks if our parent is marked as part of an implied eval argument. If
+ * so, collapses the top of impliedEvalAncestorsStack and reports on the
+ * original CallExpression.
+ * @param {ASTNode} node The CallExpression to check.
+ * @returns {boolean} True if the node matches, false if not.
+ * @private
+ */
+ function checkString(node) {
+ if (hasImpliedEvalParent(node)) {
+
+ // remove the entire substack, to avoid duplicate reports
+ const substack = impliedEvalAncestorsStack.pop();
+
+ context.report({ node: substack[0], message: "Implied eval. Consider passing a function instead of a string." });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ CallExpression(node) {
+ if (isImpliedEvalCallExpression(node)) {
+
+ // call expressions create a new substack
+ impliedEvalAncestorsStack.push([node]);
+ }
+ },
+
+ "CallExpression:exit"(node) {
+ if (node === last(last(impliedEvalAncestorsStack))) {
+
+ /*
+ * Destroys the entire sub-stack, rather than just using
+ * last(impliedEvalAncestorsStack).pop(), as a CallExpression is
+ * always the bottom of a impliedEvalAncestorsStack substack.
+ */
+ impliedEvalAncestorsStack.pop();
+ }
+ },
+
+ BinaryExpression(node) {
+ if (node.operator === "+" && hasImpliedEvalParent(node)) {
+ last(impliedEvalAncestorsStack).push(node);
+ }
+ },
+
+ "BinaryExpression:exit"(node) {
+ if (node === last(last(impliedEvalAncestorsStack))) {
+ last(impliedEvalAncestorsStack).pop();
+ }
+ },
+
+ Literal(node) {
+ if (typeof node.value === "string") {
+ checkString(node);
+ }
+ },
+
+ TemplateLiteral(node) {
+ checkString(node);
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-inline-comments.js b/tools/node_modules/eslint/lib/rules/no-inline-comments.js
new file mode 100644
index 0000000000..42b4753dfd
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-inline-comments.js
@@ -0,0 +1,65 @@
+/**
+ * @fileoverview Enforces or disallows inline comments.
+ * @author Greg Cochard
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow inline comments after code",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Will check that comments are not on lines starting with or ending with code
+ * @param {ASTNode} node The comment node to check
+ * @private
+ * @returns {void}
+ */
+ function testCodeAroundComment(node) {
+
+ // Get the whole line and cut it off at the start of the comment
+ const startLine = String(sourceCode.lines[node.loc.start.line - 1]);
+ const endLine = String(sourceCode.lines[node.loc.end.line - 1]);
+
+ const preamble = startLine.slice(0, node.loc.start.column).trim();
+
+ // Also check after the comment
+ const postamble = endLine.slice(node.loc.end.column).trim();
+
+ // Check that this comment isn't an ESLint directive
+ const isDirective = astUtils.isDirectiveComment(node);
+
+ // Should be empty if there was only whitespace around the comment
+ if (!isDirective && (preamble || postamble)) {
+ context.report({ node, message: "Unexpected comment inline with code." });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program() {
+ const comments = sourceCode.getAllComments();
+
+ comments.filter(token => token.type !== "Shebang").forEach(testCodeAroundComment);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-inner-declarations.js b/tools/node_modules/eslint/lib/rules/no-inner-declarations.js
new file mode 100644
index 0000000000..28aa5b4b5c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-inner-declarations.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview Rule to enforce declarations in program or function body root.
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow variable or `function` declarations in nested blocks",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ enum: ["functions", "both"]
+ }
+ ]
+ },
+
+ create(context) {
+
+ /**
+ * Find the nearest Program or Function ancestor node.
+ * @returns {Object} Ancestor's type and distance from node.
+ */
+ function nearestBody() {
+ const ancestors = context.getAncestors();
+ let ancestor = ancestors.pop(),
+ generation = 1;
+
+ while (ancestor && ["Program", "FunctionDeclaration",
+ "FunctionExpression", "ArrowFunctionExpression"
+ ].indexOf(ancestor.type) < 0) {
+ generation += 1;
+ ancestor = ancestors.pop();
+ }
+
+ return {
+
+ // Type of containing ancestor
+ type: ancestor.type,
+
+ // Separation between ancestor and node
+ distance: generation
+ };
+ }
+
+ /**
+ * Ensure that a given node is at a program or function body's root.
+ * @param {ASTNode} node Declaration node to check.
+ * @returns {void}
+ */
+ function check(node) {
+ const body = nearestBody(),
+ valid = ((body.type === "Program" && body.distance === 1) ||
+ body.distance === 2);
+
+ if (!valid) {
+ context.report({
+ node,
+ message: "Move {{type}} declaration to {{body}} root.",
+ data: {
+ type: (node.type === "FunctionDeclaration" ? "function" : "variable"),
+ body: (body.type === "Program" ? "program" : "function body")
+ }
+ });
+ }
+ }
+
+ return {
+
+ FunctionDeclaration: check,
+ VariableDeclaration(node) {
+ if (context.options[0] === "both" && node.kind === "var") {
+ check(node);
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-invalid-regexp.js b/tools/node_modules/eslint/lib/rules/no-invalid-regexp.js
new file mode 100644
index 0000000000..45596f7ee8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-invalid-regexp.js
@@ -0,0 +1,106 @@
+/**
+ * @fileoverview Validate strings passed to the RegExp constructor
+ * @author Michael Ficarra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const espree = require("espree");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow invalid regular expression strings in `RegExp` constructors",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [{
+ type: "object",
+ properties: {
+ allowConstructorFlags: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+
+ const options = context.options[0];
+ let allowedFlags = "";
+
+ if (options && options.allowConstructorFlags) {
+ allowedFlags = options.allowConstructorFlags.join("");
+ }
+
+ /**
+ * Check if node is a string
+ * @param {ASTNode} node node to evaluate
+ * @returns {boolean} True if its a string
+ * @private
+ */
+ function isString(node) {
+ return node && node.type === "Literal" && typeof node.value === "string";
+ }
+
+ /**
+ * Validate strings passed to the RegExp constructor
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function check(node) {
+ if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0])) {
+ let flags = isString(node.arguments[1]) ? node.arguments[1].value : "";
+
+ if (allowedFlags) {
+ flags = flags.replace(new RegExp(`[${allowedFlags}]`, "gi"), "");
+ }
+
+ try {
+ void new RegExp(node.arguments[0].value);
+ } catch (e) {
+ context.report({
+ node,
+ message: "{{message}}.",
+ data: e
+ });
+ }
+
+ if (flags) {
+
+ try {
+ espree.parse(`/./${flags}`, context.parserOptions);
+ } catch (ex) {
+ context.report({
+ node,
+ message: "Invalid flags supplied to RegExp constructor '{{flags}}'.",
+ data: {
+ flags
+ }
+ });
+ }
+ }
+
+ }
+ }
+
+ return {
+ CallExpression: check,
+ NewExpression: check
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-invalid-this.js b/tools/node_modules/eslint/lib/rules/no-invalid-this.js
new file mode 100644
index 0000000000..5a0a62f7a1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-invalid-this.js
@@ -0,0 +1,123 @@
+/**
+ * @fileoverview A rule to disallow `this` keywords outside of classes or class-like objects.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `this` keywords outside of classes or class-like objects",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const stack = [],
+ sourceCode = context.getSourceCode();
+
+ /**
+ * Gets the current checking context.
+ *
+ * The return value has a flag that whether or not `this` keyword is valid.
+ * The flag is initialized when got at the first time.
+ *
+ * @returns {{valid: boolean}}
+ * an object which has a flag that whether or not `this` keyword is valid.
+ */
+ stack.getCurrent = function() {
+ const current = this[this.length - 1];
+
+ if (!current.init) {
+ current.init = true;
+ current.valid = !astUtils.isDefaultThisBinding(
+ current.node,
+ sourceCode
+ );
+ }
+ return current;
+ };
+
+ /**
+ * Pushs new checking context into the stack.
+ *
+ * The checking context is not initialized yet.
+ * Because most functions don't have `this` keyword.
+ * When `this` keyword was found, the checking context is initialized.
+ *
+ * @param {ASTNode} node - A function node that was entered.
+ * @returns {void}
+ */
+ function enterFunction(node) {
+
+ // `this` can be invalid only under strict mode.
+ stack.push({
+ init: !context.getScope().isStrict,
+ node,
+ valid: true
+ });
+ }
+
+ /**
+ * Pops the current checking context from the stack.
+ * @returns {void}
+ */
+ function exitFunction() {
+ stack.pop();
+ }
+
+ return {
+
+ /*
+ * `this` is invalid only under strict mode.
+ * Modules is always strict mode.
+ */
+ Program(node) {
+ const scope = context.getScope(),
+ features = context.parserOptions.ecmaFeatures || {};
+
+ stack.push({
+ init: true,
+ node,
+ valid: !(
+ scope.isStrict ||
+ node.sourceType === "module" ||
+ (features.globalReturn && scope.childScopes[0].isStrict)
+ )
+ });
+ },
+
+ "Program:exit"() {
+ stack.pop();
+ },
+
+ FunctionDeclaration: enterFunction,
+ "FunctionDeclaration:exit": exitFunction,
+ FunctionExpression: enterFunction,
+ "FunctionExpression:exit": exitFunction,
+
+ // Reports if `this` of the current context is invalid.
+ ThisExpression(node) {
+ const current = stack.getCurrent();
+
+ if (current && !current.valid) {
+ context.report({ node, message: "Unexpected 'this'." });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-irregular-whitespace.js b/tools/node_modules/eslint/lib/rules/no-irregular-whitespace.js
new file mode 100644
index 0000000000..cfbdfd1a5e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-irregular-whitespace.js
@@ -0,0 +1,236 @@
+/**
+ * @fileoverview Rule to disalow whitespace that is not a tab or space, whitespace inside strings and comments are allowed
+ * @author Jonathan Kingston
+ * @author Christophe Porteneuve
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const ALL_IRREGULARS = /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/;
+const IRREGULAR_WHITESPACE = /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg;
+const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/mg;
+const LINE_BREAK = astUtils.createGlobalLinebreakMatcher();
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow irregular whitespace outside of strings and comments",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ skipComments: {
+ type: "boolean"
+ },
+ skipStrings: {
+ type: "boolean"
+ },
+ skipTemplates: {
+ type: "boolean"
+ },
+ skipRegExps: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ // Module store of errors that we have found
+ let errors = [];
+
+ // Lookup the `skipComments` option, which defaults to `false`.
+ const options = context.options[0] || {};
+ const skipComments = !!options.skipComments;
+ const skipStrings = options.skipStrings !== false;
+ const skipRegExps = !!options.skipRegExps;
+ const skipTemplates = !!options.skipTemplates;
+
+ const sourceCode = context.getSourceCode();
+ const commentNodes = sourceCode.getAllComments();
+
+ /**
+ * Removes errors that occur inside a string node
+ * @param {ASTNode} node to check for matching errors.
+ * @returns {void}
+ * @private
+ */
+ function removeWhitespaceError(node) {
+ const locStart = node.loc.start;
+ const locEnd = node.loc.end;
+
+ errors = errors.filter(error => {
+ const errorLoc = error[1];
+
+ if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) {
+ if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+ /**
+ * Checks identifier or literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
+ * @param {ASTNode} node to check for matching errors.
+ * @returns {void}
+ * @private
+ */
+ function removeInvalidNodeErrorsInIdentifierOrLiteral(node) {
+ const shouldCheckStrings = skipStrings && (typeof node.value === "string");
+ const shouldCheckRegExps = skipRegExps && (node.value instanceof RegExp);
+
+ if (shouldCheckStrings || shouldCheckRegExps) {
+
+ // If we have irregular characters remove them from the errors list
+ if (ALL_IRREGULARS.test(node.raw)) {
+ removeWhitespaceError(node);
+ }
+ }
+ }
+
+ /**
+ * Checks template string literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
+ * @param {ASTNode} node to check for matching errors.
+ * @returns {void}
+ * @private
+ */
+ function removeInvalidNodeErrorsInTemplateLiteral(node) {
+ if (typeof node.value.raw === "string") {
+ if (ALL_IRREGULARS.test(node.value.raw)) {
+ removeWhitespaceError(node);
+ }
+ }
+ }
+
+ /**
+ * Checks comment nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
+ * @param {ASTNode} node to check for matching errors.
+ * @returns {void}
+ * @private
+ */
+ function removeInvalidNodeErrorsInComment(node) {
+ if (ALL_IRREGULARS.test(node.value)) {
+ removeWhitespaceError(node);
+ }
+ }
+
+ /**
+ * Checks the program source for irregular whitespace
+ * @param {ASTNode} node The program node
+ * @returns {void}
+ * @private
+ */
+ function checkForIrregularWhitespace(node) {
+ const sourceLines = sourceCode.lines;
+
+ sourceLines.forEach((sourceLine, lineIndex) => {
+ const lineNumber = lineIndex + 1;
+ let match;
+
+ while ((match = IRREGULAR_WHITESPACE.exec(sourceLine)) !== null) {
+ const location = {
+ line: lineNumber,
+ column: match.index
+ };
+
+ errors.push([node, location, "Irregular whitespace not allowed."]);
+ }
+ });
+ }
+
+ /**
+ * Checks the program source for irregular line terminators
+ * @param {ASTNode} node The program node
+ * @returns {void}
+ * @private
+ */
+ function checkForIrregularLineTerminators(node) {
+ const source = sourceCode.getText(),
+ sourceLines = sourceCode.lines,
+ linebreaks = source.match(LINE_BREAK);
+ let lastLineIndex = -1,
+ match;
+
+ while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) {
+ const lineIndex = linebreaks.indexOf(match[0], lastLineIndex + 1) || 0;
+ const location = {
+ line: lineIndex + 1,
+ column: sourceLines[lineIndex].length
+ };
+
+ errors.push([node, location, "Irregular whitespace not allowed."]);
+ lastLineIndex = lineIndex;
+ }
+ }
+
+ /**
+ * A no-op function to act as placeholder for comment accumulation when the `skipComments` option is `false`.
+ * @returns {void}
+ * @private
+ */
+ function noop() {}
+
+ const nodes = {};
+
+ if (ALL_IRREGULARS.test(sourceCode.getText())) {
+ nodes.Program = function(node) {
+
+ /*
+ * As we can easily fire warnings for all white space issues with
+ * all the source its simpler to fire them here.
+ * This means we can check all the application code without having
+ * to worry about issues caused in the parser tokens.
+ * When writing this code also evaluating per node was missing out
+ * connecting tokens in some cases.
+ * We can later filter the errors when they are found to be not an
+ * issue in nodes we don't care about.
+ */
+ checkForIrregularWhitespace(node);
+ checkForIrregularLineTerminators(node);
+ };
+
+ nodes.Identifier = removeInvalidNodeErrorsInIdentifierOrLiteral;
+ nodes.Literal = removeInvalidNodeErrorsInIdentifierOrLiteral;
+ nodes.TemplateElement = skipTemplates ? removeInvalidNodeErrorsInTemplateLiteral : noop;
+ nodes["Program:exit"] = function() {
+ if (skipComments) {
+
+ // First strip errors occurring in comment nodes.
+ commentNodes.forEach(removeInvalidNodeErrorsInComment);
+ }
+
+ // If we have any errors remaining report on them
+ errors.forEach(error => {
+ context.report.apply(context, error);
+ });
+ };
+ } else {
+ nodes.Program = noop;
+ }
+
+ return nodes;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-iterator.js b/tools/node_modules/eslint/lib/rules/no-iterator.js
new file mode 100644
index 0000000000..3677dd94d9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-iterator.js
@@ -0,0 +1,38 @@
+/**
+ * @fileoverview Rule to flag usage of __iterator__ property
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of the `__iterator__` property",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ MemberExpression(node) {
+
+ if (node.property &&
+ (node.property.type === "Identifier" && node.property.name === "__iterator__" && !node.computed) ||
+ (node.property.type === "Literal" && node.property.value === "__iterator__")) {
+ context.report({ node, message: "Reserved name '__iterator__'." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-label-var.js b/tools/node_modules/eslint/lib/rules/no-label-var.js
new file mode 100644
index 0000000000..954066aef3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-label-var.js
@@ -0,0 +1,69 @@
+/**
+ * @fileoverview Rule to flag labels that are the same as an identifier
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow labels that share a name with a variable",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Check if the identifier is present inside current scope
+ * @param {Object} scope current scope
+ * @param {string} name To evaluate
+ * @returns {boolean} True if its present
+ * @private
+ */
+ function findIdentifier(scope, name) {
+ return astUtils.getVariableByName(scope, name) !== null;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+
+ LabeledStatement(node) {
+
+ // Fetch the innermost scope.
+ const scope = context.getScope();
+
+ /*
+ * Recursively find the identifier walking up the scope, starting
+ * with the innermost scope.
+ */
+ if (findIdentifier(scope, node.label.name)) {
+ context.report({ node, message: "Found identifier with same name as label." });
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-labels.js b/tools/node_modules/eslint/lib/rules/no-labels.js
new file mode 100644
index 0000000000..101092a667
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-labels.js
@@ -0,0 +1,141 @@
+/**
+ * @fileoverview Disallow Labeled Statements
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow labeled statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowLoop: {
+ type: "boolean"
+ },
+ allowSwitch: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0];
+ const allowLoop = Boolean(options && options.allowLoop);
+ const allowSwitch = Boolean(options && options.allowSwitch);
+ let scopeInfo = null;
+
+ /**
+ * Gets the kind of a given node.
+ *
+ * @param {ASTNode} node - A node to get.
+ * @returns {string} The kind of the node.
+ */
+ function getBodyKind(node) {
+ if (astUtils.isLoop(node)) {
+ return "loop";
+ }
+ if (node.type === "SwitchStatement") {
+ return "switch";
+ }
+ return "other";
+ }
+
+ /**
+ * Checks whether the label of a given kind is allowed or not.
+ *
+ * @param {string} kind - A kind to check.
+ * @returns {boolean} `true` if the kind is allowed.
+ */
+ function isAllowed(kind) {
+ switch (kind) {
+ case "loop": return allowLoop;
+ case "switch": return allowSwitch;
+ default: return false;
+ }
+ }
+
+ /**
+ * Checks whether a given name is a label of a loop or not.
+ *
+ * @param {string} label - A name of a label to check.
+ * @returns {boolean} `true` if the name is a label of a loop.
+ */
+ function getKind(label) {
+ let info = scopeInfo;
+
+ while (info) {
+ if (info.label === label) {
+ return info.kind;
+ }
+ info = info.upper;
+ }
+
+ /* istanbul ignore next: syntax error */
+ return "other";
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ LabeledStatement(node) {
+ scopeInfo = {
+ label: node.label.name,
+ kind: getBodyKind(node.body),
+ upper: scopeInfo
+ };
+ },
+
+ "LabeledStatement:exit"(node) {
+ if (!isAllowed(scopeInfo.kind)) {
+ context.report({
+ node,
+ message: "Unexpected labeled statement."
+ });
+ }
+
+ scopeInfo = scopeInfo.upper;
+ },
+
+ BreakStatement(node) {
+ if (node.label && !isAllowed(getKind(node.label.name))) {
+ context.report({
+ node,
+ message: "Unexpected label in break statement."
+ });
+ }
+ },
+
+ ContinueStatement(node) {
+ if (node.label && !isAllowed(getKind(node.label.name))) {
+ context.report({
+ node,
+ message: "Unexpected label in continue statement."
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-lone-blocks.js b/tools/node_modules/eslint/lib/rules/no-lone-blocks.js
new file mode 100644
index 0000000000..2b5666e213
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-lone-blocks.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Rule to flag blocks with no reason to exist
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary nested blocks",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ // A stack of lone blocks to be checked for block-level bindings
+ const loneBlocks = [];
+ let ruleDef;
+
+ /**
+ * Reports a node as invalid.
+ * @param {ASTNode} node - The node to be reported.
+ * @returns {void}
+ */
+ function report(node) {
+ const message = node.parent.type === "BlockStatement" ? "Nested block is redundant." : "Block is redundant.";
+
+ context.report({ node, message });
+ }
+
+ /**
+ * Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear
+ * @param {ASTNode} node The node to check
+ * @returns {boolean} True if the node is a lone block.
+ */
+ function isLoneBlock(node) {
+ return node.parent.type === "BlockStatement" ||
+ node.parent.type === "Program" ||
+
+ // Don't report blocks in switch cases if the block is the only statement of the case.
+ node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1);
+ }
+
+ /**
+ * Checks the enclosing block of the current node for block-level bindings,
+ * and "marks it" as valid if any.
+ * @returns {void}
+ */
+ function markLoneBlock() {
+ if (loneBlocks.length === 0) {
+ return;
+ }
+
+ const block = context.getAncestors().pop();
+
+ if (loneBlocks[loneBlocks.length - 1] === block) {
+ loneBlocks.pop();
+ }
+ }
+
+ // Default rule definition: report all lone blocks
+ ruleDef = {
+ BlockStatement(node) {
+ if (isLoneBlock(node)) {
+ report(node);
+ }
+ }
+ };
+
+ // ES6: report blocks without block-level bindings
+ if (context.parserOptions.ecmaVersion >= 6) {
+ ruleDef = {
+ BlockStatement(node) {
+ if (isLoneBlock(node)) {
+ loneBlocks.push(node);
+ }
+ },
+ "BlockStatement:exit"(node) {
+ if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
+ loneBlocks.pop();
+ report(node);
+ }
+ }
+ };
+
+ ruleDef.VariableDeclaration = function(node) {
+ if (node.kind === "let" || node.kind === "const") {
+ markLoneBlock();
+ }
+ };
+
+ ruleDef.FunctionDeclaration = function() {
+ if (context.getScope().isStrict) {
+ markLoneBlock();
+ }
+ };
+
+ ruleDef.ClassDeclaration = markLoneBlock;
+ }
+
+ return ruleDef;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-lonely-if.js b/tools/node_modules/eslint/lib/rules/no-lonely-if.js
new file mode 100644
index 0000000000..db127d1945
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-lonely-if.js
@@ -0,0 +1,83 @@
+/**
+ * @fileoverview Rule to disallow if as the only statmenet in an else block
+ * @author Brandon Mills
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `if` statements as the only statement in `else` blocks",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ IfStatement(node) {
+ const ancestors = context.getAncestors(),
+ parent = ancestors.pop(),
+ grandparent = ancestors.pop();
+
+ if (parent && parent.type === "BlockStatement" &&
+ parent.body.length === 1 && grandparent &&
+ grandparent.type === "IfStatement" &&
+ parent === grandparent.alternate) {
+ context.report({
+ node,
+ message: "Unexpected if as the only statement in an else block.",
+ fix(fixer) {
+ const openingElseCurly = sourceCode.getFirstToken(parent);
+ const closingElseCurly = sourceCode.getLastToken(parent);
+ const elseKeyword = sourceCode.getTokenBefore(openingElseCurly);
+ const tokenAfterElseBlock = sourceCode.getTokenAfter(closingElseCurly);
+ const lastIfToken = sourceCode.getLastToken(node.consequent);
+ const sourceText = sourceCode.getText();
+
+ if (sourceText.slice(openingElseCurly.range[1],
+ node.range[0]).trim() || sourceText.slice(node.range[1], closingElseCurly.range[0]).trim()) {
+
+ // Don't fix if there are any non-whitespace characters interfering (e.g. comments)
+ return null;
+ }
+
+ if (
+ node.consequent.type !== "BlockStatement" && lastIfToken.value !== ";" && tokenAfterElseBlock &&
+ (
+ node.consequent.loc.end.line === tokenAfterElseBlock.loc.start.line ||
+ /^[([/+`-]/.test(tokenAfterElseBlock.value) ||
+ lastIfToken.value === "++" ||
+ lastIfToken.value === "--"
+ )
+ ) {
+
+ /*
+ * If the `if` statement has no block, and is not followed by a semicolon, make sure that fixing
+ * the issue would not change semantics due to ASI. If this would happen, don't do a fix.
+ */
+ return null;
+ }
+
+ return fixer.replaceTextRange(
+ [openingElseCurly.range[0], closingElseCurly.range[1]],
+ (elseKeyword.range[1] === openingElseCurly.range[0] ? " " : "") + sourceCode.getText(node)
+ );
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-loop-func.js b/tools/node_modules/eslint/lib/rules/no-loop-func.js
new file mode 100644
index 0000000000..c97e0c3c5a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-loop-func.js
@@ -0,0 +1,201 @@
+/**
+ * @fileoverview Rule to flag creation of function inside a loop
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets the containing loop node of a specified node.
+ *
+ * We don't need to check nested functions, so this ignores those.
+ * `Scope.through` contains references of nested functions.
+ *
+ * @param {ASTNode} node - An AST node to get.
+ * @returns {ASTNode|null} The containing loop node of the specified node, or
+ * `null`.
+ */
+function getContainingLoopNode(node) {
+ let parent = node.parent;
+
+ while (parent) {
+ switch (parent.type) {
+ case "WhileStatement":
+ case "DoWhileStatement":
+ return parent;
+
+ case "ForStatement":
+
+ // `init` is outside of the loop.
+ if (parent.init !== node) {
+ return parent;
+ }
+ break;
+
+ case "ForInStatement":
+ case "ForOfStatement":
+
+ // `right` is outside of the loop.
+ if (parent.right !== node) {
+ return parent;
+ }
+ break;
+
+ case "ArrowFunctionExpression":
+ case "FunctionExpression":
+ case "FunctionDeclaration":
+
+ // We don't need to check nested functions.
+ return null;
+
+ default:
+ break;
+ }
+
+ node = parent;
+ parent = node.parent;
+ }
+
+ return null;
+}
+
+/**
+ * Gets the containing loop node of a given node.
+ * If the loop was nested, this returns the most outer loop.
+ *
+ * @param {ASTNode} node - A node to get. This is a loop node.
+ * @param {ASTNode|null} excludedNode - A node that the result node should not
+ * include.
+ * @returns {ASTNode} The most outer loop node.
+ */
+function getTopLoopNode(node, excludedNode) {
+ let retv = node;
+ const border = excludedNode ? excludedNode.range[1] : 0;
+
+ while (node && node.range[0] >= border) {
+ retv = node;
+ node = getContainingLoopNode(node);
+ }
+
+ return retv;
+}
+
+/**
+ * Checks whether a given reference which refers to an upper scope's variable is
+ * safe or not.
+ *
+ * @param {ASTNode} loopNode - A containing loop node.
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the reference is safe or not.
+ */
+function isSafe(loopNode, reference) {
+ const variable = reference.resolved;
+ const definition = variable && variable.defs[0];
+ const declaration = definition && definition.parent;
+ const kind = (declaration && declaration.type === "VariableDeclaration")
+ ? declaration.kind
+ : "";
+
+ // Variables which are declared by `const` is safe.
+ if (kind === "const") {
+ return true;
+ }
+
+ /*
+ * Variables which are declared by `let` in the loop is safe.
+ * It's a different instance from the next loop step's.
+ */
+ if (kind === "let" &&
+ declaration.range[0] > loopNode.range[0] &&
+ declaration.range[1] < loopNode.range[1]
+ ) {
+ return true;
+ }
+
+ /*
+ * WriteReferences which exist after this border are unsafe because those
+ * can modify the variable.
+ */
+ const border = getTopLoopNode(
+ loopNode,
+ (kind === "let") ? declaration : null
+ ).range[0];
+
+ /**
+ * Checks whether a given reference is safe or not.
+ * The reference is every reference of the upper scope's variable we are
+ * looking now.
+ *
+ * It's safeafe if the reference matches one of the following condition.
+ * - is readonly.
+ * - doesn't exist inside a local function and after the border.
+ *
+ * @param {eslint-scope.Reference} upperRef - A reference to check.
+ * @returns {boolean} `true` if the reference is safe.
+ */
+ function isSafeReference(upperRef) {
+ const id = upperRef.identifier;
+
+ return (
+ !upperRef.isWrite() ||
+ variable.scope.variableScope === upperRef.from.variableScope &&
+ id.range[0] < border
+ );
+ }
+
+ return Boolean(variable) && variable.references.every(isSafeReference);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `function` declarations and expressions inside loop statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Reports functions which match the following condition:
+ *
+ * - has a loop node in ancestors.
+ * - has any references which refers to an unsafe variable.
+ *
+ * @param {ASTNode} node The AST node to check.
+ * @returns {boolean} Whether or not the node is within a loop.
+ */
+ function checkForLoops(node) {
+ const loopNode = getContainingLoopNode(node);
+
+ if (!loopNode) {
+ return;
+ }
+
+ const references = context.getScope().through;
+
+ if (references.length > 0 &&
+ !references.every(isSafe.bind(null, loopNode))
+ ) {
+ context.report({ node, message: "Don't make functions within a loop." });
+ }
+ }
+
+ return {
+ ArrowFunctionExpression: checkForLoops,
+ FunctionExpression: checkForLoops,
+ FunctionDeclaration: checkForLoops
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-magic-numbers.js b/tools/node_modules/eslint/lib/rules/no-magic-numbers.js
new file mode 100644
index 0000000000..796ecff0f8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-magic-numbers.js
@@ -0,0 +1,149 @@
+/**
+ * @fileoverview Rule to flag statements that use magic numbers (adapted from https://github.com/danielstjules/buddy.js)
+ * @author Vincent Lemeunier
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow magic numbers",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [{
+ type: "object",
+ properties: {
+ detectObjects: {
+ type: "boolean"
+ },
+ enforceConst: {
+ type: "boolean"
+ },
+ ignore: {
+ type: "array",
+ items: {
+ type: "number"
+ },
+ uniqueItems: true
+ },
+ ignoreArrayIndexes: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }]
+ },
+
+ create(context) {
+ const config = context.options[0] || {},
+ detectObjects = !!config.detectObjects,
+ enforceConst = !!config.enforceConst,
+ ignore = config.ignore || [],
+ ignoreArrayIndexes = !!config.ignoreArrayIndexes;
+
+ /**
+ * Returns whether the node is number literal
+ * @param {Node} node - the node literal being evaluated
+ * @returns {boolean} true if the node is a number literal
+ */
+ function isNumber(node) {
+ return typeof node.value === "number";
+ }
+
+ /**
+ * Returns whether the number should be ignored
+ * @param {number} num - the number
+ * @returns {boolean} true if the number should be ignored
+ */
+ function shouldIgnoreNumber(num) {
+ return ignore.indexOf(num) !== -1;
+ }
+
+ /**
+ * Returns whether the number should be ignored when used as a radix within parseInt() or Number.parseInt()
+ * @param {ASTNode} parent - the non-"UnaryExpression" parent
+ * @param {ASTNode} node - the node literal being evaluated
+ * @returns {boolean} true if the number should be ignored
+ */
+ function shouldIgnoreParseInt(parent, node) {
+ return parent.type === "CallExpression" && node === parent.arguments[1] &&
+ (parent.callee.name === "parseInt" ||
+ parent.callee.type === "MemberExpression" &&
+ parent.callee.object.name === "Number" &&
+ parent.callee.property.name === "parseInt");
+ }
+
+ /**
+ * Returns whether the number should be ignored when used to define a JSX prop
+ * @param {ASTNode} parent - the non-"UnaryExpression" parent
+ * @returns {boolean} true if the number should be ignored
+ */
+ function shouldIgnoreJSXNumbers(parent) {
+ return parent.type.indexOf("JSX") === 0;
+ }
+
+ /**
+ * Returns whether the number should be ignored when used as an array index with enabled 'ignoreArrayIndexes' option.
+ * @param {ASTNode} parent - the non-"UnaryExpression" parent.
+ * @returns {boolean} true if the number should be ignored
+ */
+ function shouldIgnoreArrayIndexes(parent) {
+ return parent.type === "MemberExpression" && ignoreArrayIndexes;
+ }
+
+ return {
+ Literal(node) {
+ let parent = node.parent,
+ value = node.value,
+ raw = node.raw;
+ const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
+
+ if (!isNumber(node)) {
+ return;
+ }
+
+ // For negative magic numbers: update the value and parent node
+ if (parent.type === "UnaryExpression" && parent.operator === "-") {
+ node = parent;
+ parent = node.parent;
+ value = -value;
+ raw = `-${raw}`;
+ }
+
+ if (shouldIgnoreNumber(value) ||
+ shouldIgnoreParseInt(parent, node) ||
+ shouldIgnoreArrayIndexes(parent) ||
+ shouldIgnoreJSXNumbers(parent)) {
+ return;
+ }
+
+ if (parent.type === "VariableDeclarator") {
+ if (enforceConst && parent.parent.kind !== "const") {
+ context.report({
+ node,
+ message: "Number constants declarations must use 'const'."
+ });
+ }
+ } else if (
+ okTypes.indexOf(parent.type) === -1 ||
+ (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
+ ) {
+ context.report({
+ node,
+ message: "No magic number: {{raw}}.",
+ data: {
+ raw
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-mixed-operators.js b/tools/node_modules/eslint/lib/rules/no-mixed-operators.js
new file mode 100644
index 0000000000..9f1fbc9a6d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-mixed-operators.js
@@ -0,0 +1,209 @@
+/**
+ * @fileoverview Rule to disallow mixed binary operators.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils.js");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const ARITHMETIC_OPERATORS = ["+", "-", "*", "/", "%", "**"];
+const BITWISE_OPERATORS = ["&", "|", "^", "~", "<<", ">>", ">>>"];
+const COMPARISON_OPERATORS = ["==", "!=", "===", "!==", ">", ">=", "<", "<="];
+const LOGICAL_OPERATORS = ["&&", "||"];
+const RELATIONAL_OPERATORS = ["in", "instanceof"];
+const ALL_OPERATORS = [].concat(
+ ARITHMETIC_OPERATORS,
+ BITWISE_OPERATORS,
+ COMPARISON_OPERATORS,
+ LOGICAL_OPERATORS,
+ RELATIONAL_OPERATORS
+);
+const DEFAULT_GROUPS = [
+ ARITHMETIC_OPERATORS,
+ BITWISE_OPERATORS,
+ COMPARISON_OPERATORS,
+ LOGICAL_OPERATORS,
+ RELATIONAL_OPERATORS
+];
+const TARGET_NODE_TYPE = /^(?:Binary|Logical)Expression$/;
+
+/**
+ * Normalizes options.
+ *
+ * @param {Object|undefined} options - A options object to normalize.
+ * @returns {Object} Normalized option object.
+ */
+function normalizeOptions(options) {
+ const hasGroups = (options && options.groups && options.groups.length > 0);
+ const groups = hasGroups ? options.groups : DEFAULT_GROUPS;
+ const allowSamePrecedence = (options && options.allowSamePrecedence) !== false;
+
+ return {
+ groups,
+ allowSamePrecedence
+ };
+}
+
+/**
+ * Checks whether any group which includes both given operator exists or not.
+ *
+ * @param {Array.<string[]>} groups - A list of groups to check.
+ * @param {string} left - An operator.
+ * @param {string} right - Another operator.
+ * @returns {boolean} `true` if such group existed.
+ */
+function includesBothInAGroup(groups, left, right) {
+ return groups.some(group => group.indexOf(left) !== -1 && group.indexOf(right) !== -1);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow mixed binary operators",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: [
+ {
+ type: "object",
+ properties: {
+ groups: {
+ type: "array",
+ items: {
+ type: "array",
+ items: { enum: ALL_OPERATORS },
+ minItems: 2,
+ uniqueItems: true
+ },
+ uniqueItems: true
+ },
+ allowSamePrecedence: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const options = normalizeOptions(context.options[0]);
+
+ /**
+ * Checks whether a given node should be ignored by options or not.
+ *
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
+ * node or a LogicalExpression node. This parent node is one of
+ * them, too.
+ * @returns {boolean} `true` if the node should be ignored.
+ */
+ function shouldIgnore(node) {
+ const a = node;
+ const b = node.parent;
+
+ return (
+ !includesBothInAGroup(options.groups, a.operator, b.operator) ||
+ (
+ options.allowSamePrecedence &&
+ astUtils.getPrecedence(a) === astUtils.getPrecedence(b)
+ )
+ );
+ }
+
+ /**
+ * Checks whether the operator of a given node is mixed with parent
+ * node's operator or not.
+ *
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
+ * node or a LogicalExpression node. This parent node is one of
+ * them, too.
+ * @returns {boolean} `true` if the node was mixed.
+ */
+ function isMixedWithParent(node) {
+ return (
+ node.operator !== node.parent.operator &&
+ !astUtils.isParenthesised(sourceCode, node)
+ );
+ }
+
+ /**
+ * Gets the operator token of a given node.
+ *
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
+ * node or a LogicalExpression node.
+ * @returns {Token} The operator token of the node.
+ */
+ function getOperatorToken(node) {
+ return sourceCode.getTokenAfter(node.left, astUtils.isNotClosingParenToken);
+ }
+
+ /**
+ * Reports both the operator of a given node and the operator of the
+ * parent node.
+ *
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
+ * node or a LogicalExpression node. This parent node is one of
+ * them, too.
+ * @returns {void}
+ */
+ function reportBothOperators(node) {
+ const parent = node.parent;
+ const left = (parent.left === node) ? node : parent;
+ const right = (parent.left !== node) ? node : parent;
+ const message =
+ "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'.";
+ const data = {
+ leftOperator: left.operator,
+ rightOperator: right.operator
+ };
+
+ context.report({
+ node: left,
+ loc: getOperatorToken(left).loc.start,
+ message,
+ data
+ });
+ context.report({
+ node: right,
+ loc: getOperatorToken(right).loc.start,
+ message,
+ data
+ });
+ }
+
+ /**
+ * Checks between the operator of this node and the operator of the
+ * parent node.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {void}
+ */
+ function check(node) {
+ if (TARGET_NODE_TYPE.test(node.parent.type) &&
+ isMixedWithParent(node) &&
+ !shouldIgnore(node)
+ ) {
+ reportBothOperators(node);
+ }
+ }
+
+ return {
+ BinaryExpression: check,
+ LogicalExpression: check
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-mixed-requires.js b/tools/node_modules/eslint/lib/rules/no-mixed-requires.js
new file mode 100644
index 0000000000..171052a52a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-mixed-requires.js
@@ -0,0 +1,220 @@
+/**
+ * @fileoverview Rule to enforce grouped require statements for Node.JS
+ * @author Raphael Pigulla
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `require` calls to be mixed with regular variable declarations",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "boolean"
+ },
+ {
+ type: "object",
+ properties: {
+ grouping: {
+ type: "boolean"
+ },
+ allowCall: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = context.options[0];
+ let grouping = false,
+ allowCall = false;
+
+ if (typeof options === "object") {
+ grouping = options.grouping;
+ allowCall = options.allowCall;
+ } else {
+ grouping = !!options;
+ }
+
+ /**
+ * Returns the list of built-in modules.
+ *
+ * @returns {string[]} An array of built-in Node.js modules.
+ */
+ function getBuiltinModules() {
+
+ /*
+ * This list is generated using:
+ * `require("repl")._builtinLibs.concat('repl').sort()`
+ * This particular list is as per nodejs v0.12.2 and iojs v0.7.1
+ */
+ return [
+ "assert", "buffer", "child_process", "cluster", "crypto",
+ "dgram", "dns", "domain", "events", "fs", "http", "https",
+ "net", "os", "path", "punycode", "querystring", "readline",
+ "repl", "smalloc", "stream", "string_decoder", "tls", "tty",
+ "url", "util", "v8", "vm", "zlib"
+ ];
+ }
+
+ const BUILTIN_MODULES = getBuiltinModules();
+
+ const DECL_REQUIRE = "require",
+ DECL_UNINITIALIZED = "uninitialized",
+ DECL_OTHER = "other";
+
+ const REQ_CORE = "core",
+ REQ_FILE = "file",
+ REQ_MODULE = "module",
+ REQ_COMPUTED = "computed";
+
+ /**
+ * Determines the type of a declaration statement.
+ * @param {ASTNode} initExpression The init node of the VariableDeclarator.
+ * @returns {string} The type of declaration represented by the expression.
+ */
+ function getDeclarationType(initExpression) {
+ if (!initExpression) {
+
+ // "var x;"
+ return DECL_UNINITIALIZED;
+ }
+
+ if (initExpression.type === "CallExpression" &&
+ initExpression.callee.type === "Identifier" &&
+ initExpression.callee.name === "require"
+ ) {
+
+ // "var x = require('util');"
+ return DECL_REQUIRE;
+ }
+ if (allowCall &&
+ initExpression.type === "CallExpression" &&
+ initExpression.callee.type === "CallExpression"
+ ) {
+
+ // "var x = require('diagnose')('sub-module');"
+ return getDeclarationType(initExpression.callee);
+ }
+ if (initExpression.type === "MemberExpression") {
+
+ // "var x = require('glob').Glob;"
+ return getDeclarationType(initExpression.object);
+ }
+
+ // "var x = 42;"
+ return DECL_OTHER;
+ }
+
+ /**
+ * Determines the type of module that is loaded via require.
+ * @param {ASTNode} initExpression The init node of the VariableDeclarator.
+ * @returns {string} The module type.
+ */
+ function inferModuleType(initExpression) {
+ if (initExpression.type === "MemberExpression") {
+
+ // "var x = require('glob').Glob;"
+ return inferModuleType(initExpression.object);
+ }
+ if (initExpression.arguments.length === 0) {
+
+ // "var x = require();"
+ return REQ_COMPUTED;
+ }
+
+ const arg = initExpression.arguments[0];
+
+ if (arg.type !== "Literal" || typeof arg.value !== "string") {
+
+ // "var x = require(42);"
+ return REQ_COMPUTED;
+ }
+
+ if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
+
+ // "var fs = require('fs');"
+ return REQ_CORE;
+ }
+ if (/^\.{0,2}\//.test(arg.value)) {
+
+ // "var utils = require('./utils');"
+ return REQ_FILE;
+ }
+
+ // "var async = require('async');"
+ return REQ_MODULE;
+
+ }
+
+ /**
+ * Check if the list of variable declarations is mixed, i.e. whether it
+ * contains both require and other declarations.
+ * @param {ASTNode} declarations The list of VariableDeclarators.
+ * @returns {boolean} True if the declarations are mixed, false if not.
+ */
+ function isMixed(declarations) {
+ const contains = {};
+
+ declarations.forEach(declaration => {
+ const type = getDeclarationType(declaration.init);
+
+ contains[type] = true;
+ });
+
+ return !!(
+ contains[DECL_REQUIRE] &&
+ (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
+ );
+ }
+
+ /**
+ * Check if all require declarations in the given list are of the same
+ * type.
+ * @param {ASTNode} declarations The list of VariableDeclarators.
+ * @returns {boolean} True if the declarations are grouped, false if not.
+ */
+ function isGrouped(declarations) {
+ const found = {};
+
+ declarations.forEach(declaration => {
+ if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
+ found[inferModuleType(declaration.init)] = true;
+ }
+ });
+
+ return Object.keys(found).length <= 1;
+ }
+
+
+ return {
+
+ VariableDeclaration(node) {
+
+ if (isMixed(node.declarations)) {
+ context.report({ node, message: "Do not mix 'require' and other declarations." });
+ } else if (grouping && !isGrouped(node.declarations)) {
+ context.report({ node, message: "Do not mix core, module, file and computed requires." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-mixed-spaces-and-tabs.js b/tools/node_modules/eslint/lib/rules/no-mixed-spaces-and-tabs.js
new file mode 100644
index 0000000000..2b8e89d3c8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-mixed-spaces-and-tabs.js
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview Disallow mixed spaces and tabs for indentation
+ * @author Jary Niebur
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow mixed spaces and tabs for indentation",
+ category: "Stylistic Issues",
+ recommended: true
+ },
+
+ schema: [
+ {
+ enum: ["smart-tabs", true, false]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ let smartTabs;
+ const ignoredLocs = [];
+
+ switch (context.options[0]) {
+ case true: // Support old syntax, maybe add deprecation warning here
+ case "smart-tabs":
+ smartTabs = true;
+ break;
+ default:
+ smartTabs = false;
+ }
+
+ /**
+ * Determines if a given line and column are before a location.
+ * @param {Location} loc The location object from an AST node.
+ * @param {int} line The line to check.
+ * @param {int} column The column to check.
+ * @returns {boolean} True if the line and column are before the location, false if not.
+ * @private
+ */
+ function beforeLoc(loc, line, column) {
+ if (line < loc.start.line) {
+ return true;
+ }
+ return line === loc.start.line && column < loc.start.column;
+ }
+
+ /**
+ * Determines if a given line and column are after a location.
+ * @param {Location} loc The location object from an AST node.
+ * @param {int} line The line to check.
+ * @param {int} column The column to check.
+ * @returns {boolean} True if the line and column are after the location, false if not.
+ * @private
+ */
+ function afterLoc(loc, line, column) {
+ if (line > loc.end.line) {
+ return true;
+ }
+ return line === loc.end.line && column > loc.end.column;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ TemplateElement(node) {
+ ignoredLocs.push(node.loc);
+ },
+
+ "Program:exit"(node) {
+
+ /*
+ * At least one space followed by a tab
+ * or the reverse before non-tab/-space
+ * characters begin.
+ */
+ let regex = /^(?=[\t ]*(\t | \t))/;
+ const lines = sourceCode.lines,
+ comments = sourceCode.getAllComments();
+
+ comments.forEach(comment => {
+ ignoredLocs.push(comment.loc);
+ });
+
+ ignoredLocs.sort((first, second) => {
+ if (beforeLoc(first, second.start.line, second.start.column)) {
+ return 1;
+ }
+
+ if (beforeLoc(second, first.start.line, second.start.column)) {
+ return -1;
+ }
+
+ return 0;
+ });
+
+ if (smartTabs) {
+
+ /*
+ * At least one space followed by a tab
+ * before non-tab/-space characters begin.
+ */
+ regex = /^(?=[\t ]* \t)/;
+ }
+
+ lines.forEach((line, i) => {
+ const match = regex.exec(line);
+
+ if (match) {
+ const lineNumber = i + 1,
+ column = match.index + 1;
+
+ for (let j = 0; j < ignoredLocs.length; j++) {
+ if (beforeLoc(ignoredLocs[j], lineNumber, column)) {
+ continue;
+ }
+ if (afterLoc(ignoredLocs[j], lineNumber, column)) {
+ continue;
+ }
+
+ return;
+ }
+
+ context.report({ node, loc: { line: lineNumber, column }, message: "Mixed spaces and tabs." });
+ }
+ });
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-multi-assign.js b/tools/node_modules/eslint/lib/rules/no-multi-assign.js
new file mode 100644
index 0000000000..164869f6dd
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-multi-assign.js
@@ -0,0 +1,41 @@
+/**
+ * @fileoverview Rule to check use of chained assignment expressions
+ * @author Stewart Rand
+ */
+
+"use strict";
+
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow use of chained assignment expressions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ AssignmentExpression(node) {
+ if (["AssignmentExpression", "VariableDeclarator"].indexOf(node.parent.type) !== -1) {
+ context.report({
+ node,
+ message: "Unexpected chained assignment."
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-multi-spaces.js b/tools/node_modules/eslint/lib/rules/no-multi-spaces.js
new file mode 100644
index 0000000000..84f1b50189
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-multi-spaces.js
@@ -0,0 +1,130 @@
+/**
+ * @fileoverview Disallow use of multiple spaces.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow multiple spaces",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "object",
+ patternProperties: {
+ "^([A-Z][a-z]*)+$": {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ },
+ ignoreEOLComments: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const options = context.options[0] || {};
+ const ignoreEOLComments = options.ignoreEOLComments;
+ const exceptions = Object.assign({ Property: true }, options.exceptions);
+ const hasExceptions = Object.keys(exceptions).filter(key => exceptions[key]).length > 0;
+
+ /**
+ * Formats value of given comment token for error message by truncating its length.
+ * @param {Token} token comment token
+ * @returns {string} formatted value
+ * @private
+ */
+ function formatReportedCommentValue(token) {
+ const valueLines = token.value.split("\n");
+ const value = valueLines[0];
+ const formattedValue = `${value.slice(0, 12)}...`;
+
+ return valueLines.length === 1 && value.length <= 12 ? value : formattedValue;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program() {
+ sourceCode.tokensAndComments.forEach((leftToken, leftIndex, tokensAndComments) => {
+ if (leftIndex === tokensAndComments.length - 1) {
+ return;
+ }
+ const rightToken = tokensAndComments[leftIndex + 1];
+
+ // Ignore tokens that don't have 2 spaces between them or are on different lines
+ if (
+ !sourceCode.text.slice(leftToken.range[1], rightToken.range[0]).includes(" ") ||
+ leftToken.loc.end.line < rightToken.loc.start.line
+ ) {
+ return;
+ }
+
+ // Ignore comments that are the last token on their line if `ignoreEOLComments` is active.
+ if (
+ ignoreEOLComments &&
+ astUtils.isCommentToken(rightToken) &&
+ (
+ leftIndex === tokensAndComments.length - 2 ||
+ rightToken.loc.end.line < tokensAndComments[leftIndex + 2].loc.start.line
+ )
+ ) {
+ return;
+ }
+
+ // Ignore tokens that are in a node in the "exceptions" object
+ if (hasExceptions) {
+ const parentNode = sourceCode.getNodeByRangeIndex(rightToken.range[0] - 1);
+
+ if (parentNode && exceptions[parentNode.type]) {
+ return;
+ }
+ }
+
+ let displayValue;
+
+ if (rightToken.type === "Block") {
+ displayValue = `/*${formatReportedCommentValue(rightToken)}*/`;
+ } else if (rightToken.type === "Line") {
+ displayValue = `//${formatReportedCommentValue(rightToken)}`;
+ } else {
+ displayValue = rightToken.value;
+ }
+
+ context.report({
+ node: rightToken,
+ loc: rightToken.loc.start,
+ message: "Multiple spaces found before '{{displayValue}}'.",
+ data: { displayValue },
+ fix: fixer => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ")
+ });
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-multi-str.js b/tools/node_modules/eslint/lib/rules/no-multi-str.js
new file mode 100644
index 0000000000..76f29cbb5a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-multi-str.js
@@ -0,0 +1,55 @@
+/**
+ * @fileoverview Rule to flag when using multiline strings
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow multiline strings",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Determines if a given node is part of JSX syntax.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} True if the node is a JSX node, false if not.
+ * @private
+ */
+ function isJSXElement(node) {
+ return node.type.indexOf("JSX") === 0;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+
+ Literal(node) {
+ if (astUtils.LINEBREAK_MATCHER.test(node.raw) && !isJSXElement(node.parent)) {
+ context.report({ node, message: "Multiline support is limited to browsers supporting ES5 only." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-multiple-empty-lines.js b/tools/node_modules/eslint/lib/rules/no-multiple-empty-lines.js
new file mode 100644
index 0000000000..9d1067c205
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-multiple-empty-lines.js
@@ -0,0 +1,136 @@
+/**
+ * @fileoverview Disallows multiple blank lines.
+ * implementation adapted from the no-trailing-spaces rule.
+ * @author Greg Cochard
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow multiple empty lines",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ max: {
+ type: "integer",
+ minimum: 0
+ },
+ maxEOF: {
+ type: "integer",
+ minimum: 0
+ },
+ maxBOF: {
+ type: "integer",
+ minimum: 0
+ }
+ },
+ required: ["max"],
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ // Use options.max or 2 as default
+ let max = 2,
+ maxEOF = max,
+ maxBOF = max;
+
+ if (context.options.length) {
+ max = context.options[0].max;
+ maxEOF = typeof context.options[0].maxEOF !== "undefined" ? context.options[0].maxEOF : max;
+ maxBOF = typeof context.options[0].maxBOF !== "undefined" ? context.options[0].maxBOF : max;
+ }
+
+ const sourceCode = context.getSourceCode();
+
+ // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue
+ const allLines = sourceCode.lines[sourceCode.lines.length - 1] === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines;
+ const templateLiteralLines = new Set();
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ TemplateLiteral(node) {
+ node.quasis.forEach(literalPart => {
+
+ // Empty lines have a semantic meaning if they're inside template literals. Don't count these as empty lines.
+ for (let ignoredLine = literalPart.loc.start.line; ignoredLine < literalPart.loc.end.line; ignoredLine++) {
+ templateLiteralLines.add(ignoredLine);
+ }
+ });
+ },
+ "Program:exit"(node) {
+ return allLines
+
+ // Given a list of lines, first get a list of line numbers that are non-empty.
+ .reduce((nonEmptyLineNumbers, line, index) => {
+ if (line.trim() || templateLiteralLines.has(index + 1)) {
+ nonEmptyLineNumbers.push(index + 1);
+ }
+ return nonEmptyLineNumbers;
+ }, [])
+
+ // Add a value at the end to allow trailing empty lines to be checked.
+ .concat(allLines.length + 1)
+
+ // Given two line numbers of non-empty lines, report the lines between if the difference is too large.
+ .reduce((lastLineNumber, lineNumber) => {
+ let message, maxAllowed;
+
+ if (lastLineNumber === 0) {
+ message = "Too many blank lines at the beginning of file. Max of {{max}} allowed.";
+ maxAllowed = maxBOF;
+ } else if (lineNumber === allLines.length + 1) {
+ message = "Too many blank lines at the end of file. Max of {{max}} allowed.";
+ maxAllowed = maxEOF;
+ } else {
+ message = "More than {{max}} blank {{pluralizedLines}} not allowed.";
+ maxAllowed = max;
+ }
+
+ if (lineNumber - lastLineNumber - 1 > maxAllowed) {
+ context.report({
+ node,
+ loc: { start: { line: lastLineNumber + 1, column: 0 }, end: { line: lineNumber, column: 0 } },
+ message,
+ data: { max: maxAllowed, pluralizedLines: maxAllowed === 1 ? "line" : "lines" },
+ fix(fixer) {
+ const rangeStart = sourceCode.getIndexFromLoc({ line: lastLineNumber + 1, column: 0 });
+
+ /*
+ * The end of the removal range is usually the start index of the next line.
+ * However, at the end of the file there is no next line, so the end of the
+ * range is just the length of the text.
+ */
+ const lineNumberAfterRemovedLines = lineNumber - maxAllowed;
+ const rangeEnd = lineNumberAfterRemovedLines <= allLines.length
+ ? sourceCode.getIndexFromLoc({ line: lineNumberAfterRemovedLines, column: 0 })
+ : sourceCode.text.length;
+
+ return fixer.removeRange([rangeStart, rangeEnd]);
+ }
+ });
+ }
+
+ return lineNumber;
+ }, 0);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-native-reassign.js b/tools/node_modules/eslint/lib/rules/no-native-reassign.js
new file mode 100644
index 0000000000..a60d4e499c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-native-reassign.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview Rule to disallow assignments to native objects or read-only global variables
+ * @author Ilya Volodin
+ * @deprecated in ESLint v3.3.0
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow assignments to native objects or read-only global variables",
+ category: "Best Practices",
+ recommended: false,
+ replacedBy: ["no-global-assign"]
+ },
+
+ deprecated: true,
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: { type: "string" },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const config = context.options[0];
+ const exceptions = (config && config.exceptions) || [];
+
+ /**
+ * Reports write references.
+ * @param {Reference} reference - A reference to check.
+ * @param {int} index - The index of the reference in the references.
+ * @param {Reference[]} references - The array that the reference belongs to.
+ * @returns {void}
+ */
+ function checkReference(reference, index, references) {
+ const identifier = reference.identifier;
+
+ if (reference.init === false &&
+ reference.isWrite() &&
+
+ /*
+ * Destructuring assignments can have multiple default value,
+ * so possibly there are multiple writeable references for the same identifier.
+ */
+ (index === 0 || references[index - 1].identifier !== identifier)
+ ) {
+ context.report({
+ node: identifier,
+ message: "Read-only global '{{name}}' should not be modified.",
+ data: identifier
+ });
+ }
+ }
+
+ /**
+ * Reports write references if a given variable is read-only builtin.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
+ variable.references.forEach(checkReference);
+ }
+ }
+
+ return {
+ Program() {
+ const globalScope = context.getScope();
+
+ globalScope.variables.forEach(checkVariable);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-negated-condition.js b/tools/node_modules/eslint/lib/rules/no-negated-condition.js
new file mode 100644
index 0000000000..8ea8559ea1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-negated-condition.js
@@ -0,0 +1,82 @@
+/**
+ * @fileoverview Rule to disallow a negated condition
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow negated conditions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Determines if a given node is an if-else without a condition on the else
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} True if the node has an else without an if.
+ * @private
+ */
+ function hasElseWithoutCondition(node) {
+ return node.alternate && node.alternate.type !== "IfStatement";
+ }
+
+ /**
+ * Determines if a given node is a negated unary expression
+ * @param {Object} test The test object to check.
+ * @returns {boolean} True if the node is a negated unary expression.
+ * @private
+ */
+ function isNegatedUnaryExpression(test) {
+ return test.type === "UnaryExpression" && test.operator === "!";
+ }
+
+ /**
+ * Determines if a given node is a negated binary expression
+ * @param {Test} test The test to check.
+ * @returns {boolean} True if the node is a negated binary expression.
+ * @private
+ */
+ function isNegatedBinaryExpression(test) {
+ return test.type === "BinaryExpression" &&
+ (test.operator === "!=" || test.operator === "!==");
+ }
+
+ /**
+ * Determines if a given node has a negated if expression
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} True if the node has a negated if expression.
+ * @private
+ */
+ function isNegatedIf(node) {
+ return isNegatedUnaryExpression(node.test) || isNegatedBinaryExpression(node.test);
+ }
+
+ return {
+ IfStatement(node) {
+ if (!hasElseWithoutCondition(node)) {
+ return;
+ }
+
+ if (isNegatedIf(node)) {
+ context.report({ node, message: "Unexpected negated condition." });
+ }
+ },
+ ConditionalExpression(node) {
+ if (isNegatedIf(node)) {
+ context.report({ node, message: "Unexpected negated condition." });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-negated-in-lhs.js b/tools/node_modules/eslint/lib/rules/no-negated-in-lhs.js
new file mode 100644
index 0000000000..495cbc160e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-negated-in-lhs.js
@@ -0,0 +1,38 @@
+/**
+ * @fileoverview A rule to disallow negated left operands of the `in` operator
+ * @author Michael Ficarra
+ * @deprecated in ESLint v3.3.0
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow negating the left operand in `in` expressions",
+ category: "Possible Errors",
+ recommended: false,
+ replacedBy: ["no-unsafe-negation"]
+ },
+ deprecated: true,
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ BinaryExpression(node) {
+ if (node.operator === "in" && node.left.type === "UnaryExpression" && node.left.operator === "!") {
+ context.report({ node, message: "The 'in' expression's left operand is negated." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-nested-ternary.js b/tools/node_modules/eslint/lib/rules/no-nested-ternary.js
new file mode 100644
index 0000000000..4fe49fc9c0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-nested-ternary.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview Rule to flag nested ternary expressions
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow nested ternary expressions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ ConditionalExpression(node) {
+ if (node.alternate.type === "ConditionalExpression" ||
+ node.consequent.type === "ConditionalExpression") {
+ context.report({ node, message: "Do not nest ternary expressions." });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-new-func.js b/tools/node_modules/eslint/lib/rules/no-new-func.js
new file mode 100644
index 0000000000..6abbe8391d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-new-func.js
@@ -0,0 +1,45 @@
+/**
+ * @fileoverview Rule to flag when using new Function
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `new` operators with the `Function` object",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports a node.
+ * @param {ASTNode} node The node to report
+ * @returns {void}
+ * @private
+ */
+ function report(node) {
+ context.report({ node, message: "The Function constructor is eval." });
+ }
+
+ return {
+ "NewExpression[callee.name = 'Function']": report,
+ "CallExpression[callee.name = 'Function']": report
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-new-object.js b/tools/node_modules/eslint/lib/rules/no-new-object.js
new file mode 100644
index 0000000000..d4d77b5514
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-new-object.js
@@ -0,0 +1,35 @@
+/**
+ * @fileoverview A rule to disallow calls to the Object constructor
+ * @author Matt DuVall <http://www.mattduvall.com/>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `Object` constructors",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ NewExpression(node) {
+ if (node.callee.name === "Object") {
+ context.report({ node, message: "The object literal notation {} is preferrable." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-new-require.js b/tools/node_modules/eslint/lib/rules/no-new-require.js
new file mode 100644
index 0000000000..f9ea1f84bf
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-new-require.js
@@ -0,0 +1,35 @@
+/**
+ * @fileoverview Rule to disallow use of new operator with the `require` function
+ * @author Wil Moore III
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `new` operators with calls to `require`",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ NewExpression(node) {
+ if (node.callee.type === "Identifier" && node.callee.name === "require") {
+ context.report({ node, message: "Unexpected use of new with require." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-new-symbol.js b/tools/node_modules/eslint/lib/rules/no-new-symbol.js
new file mode 100644
index 0000000000..5743a4748a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-new-symbol.js
@@ -0,0 +1,43 @@
+/**
+ * @fileoverview Rule to disallow use of the new operator with the `Symbol` object
+ * @author Alberto Rodríguez
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `new` operators with the `Symbol` object",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ "Program:exit"() {
+ const globalScope = context.getScope();
+ const variable = globalScope.set.get("Symbol");
+
+ if (variable && variable.defs.length === 0) {
+ variable.references.forEach(ref => {
+ const node = ref.identifier;
+
+ if (node.parent && node.parent.type === "NewExpression") {
+ context.report({ node, message: "`Symbol` cannot be called as a constructor." });
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-new-wrappers.js b/tools/node_modules/eslint/lib/rules/no-new-wrappers.js
new file mode 100644
index 0000000000..65bf79b87c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-new-wrappers.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Rule to flag when using constructor for wrapper objects
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `new` operators with the `String`, `Number`, and `Boolean` objects",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ NewExpression(node) {
+ const wrapperObjects = ["String", "Number", "Boolean", "Math", "JSON"];
+
+ if (wrapperObjects.indexOf(node.callee.name) > -1) {
+ context.report({ node, message: "Do not use {{fn}} as a constructor.", data: { fn: node.callee.name } });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-new.js b/tools/node_modules/eslint/lib/rules/no-new.js
new file mode 100644
index 0000000000..6e6025aac5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-new.js
@@ -0,0 +1,33 @@
+/**
+ * @fileoverview Rule to flag statements with function invocation preceded by
+ * "new" and not part of assignment
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `new` operators outside of assignments or comparisons",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ "ExpressionStatement > NewExpression"(node) {
+ context.report({ node: node.parent, message: "Do not use 'new' for side effects." });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-obj-calls.js b/tools/node_modules/eslint/lib/rules/no-obj-calls.js
new file mode 100644
index 0000000000..0ca8a5effb
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-obj-calls.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
+ * @author James Allardice
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow calling global object properties as functions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ CallExpression(node) {
+
+ if (node.callee.type === "Identifier") {
+ const name = node.callee.name;
+
+ if (name === "Math" || name === "JSON" || name === "Reflect") {
+ context.report({ node, message: "'{{name}}' is not a function.", data: { name } });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-octal-escape.js b/tools/node_modules/eslint/lib/rules/no-octal-escape.js
new file mode 100644
index 0000000000..04bfb6aae3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-octal-escape.js
@@ -0,0 +1,47 @@
+/**
+ * @fileoverview Rule to flag octal escape sequences in string literals.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow octal escape sequences in string literals",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ Literal(node) {
+ if (typeof node.value !== "string") {
+ return;
+ }
+
+ const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-3][0-7]{1,2}|[4-7][0-7]|[0-7])/);
+
+ if (match) {
+ const octalDigit = match[2];
+
+ // \0 is actually not considered an octal
+ if (match[2] !== "0" || typeof match[3] !== "undefined") {
+ context.report({ node, message: "Don't use octal: '\\{{octalDigit}}'. Use '\\u....' instead.", data: { octalDigit } });
+ }
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-octal.js b/tools/node_modules/eslint/lib/rules/no-octal.js
new file mode 100644
index 0000000000..58082d0d1c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-octal.js
@@ -0,0 +1,35 @@
+/**
+ * @fileoverview Rule to flag when initializing octal literal
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow octal literals",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ Literal(node) {
+ if (typeof node.value === "number" && /^0[0-7]/.test(node.raw)) {
+ context.report({ node, message: "Octal literals should not be used." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-param-reassign.js b/tools/node_modules/eslint/lib/rules/no-param-reassign.js
new file mode 100644
index 0000000000..f32e42ae2f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-param-reassign.js
@@ -0,0 +1,173 @@
+/**
+ * @fileoverview Disallow reassignment of function parameters.
+ * @author Nat Burns
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/;
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow reassigning `function` parameters",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ type: "object",
+ properties: {
+ props: {
+ enum: [false]
+ }
+ },
+ additionalProperties: false
+ },
+ {
+ type: "object",
+ properties: {
+ props: {
+ enum: [true]
+ },
+ ignorePropertyModificationsFor: {
+ type: "array",
+ items: {
+ type: "string"
+ },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const props = context.options[0] && Boolean(context.options[0].props);
+ const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];
+
+ /**
+ * Checks whether or not the reference modifies properties of its variable.
+ * @param {Reference} reference - A reference to check.
+ * @returns {boolean} Whether or not the reference modifies properties of its variable.
+ */
+ function isModifyingProp(reference) {
+ let node = reference.identifier;
+ let parent = node.parent;
+
+ while (parent && !stopNodePattern.test(parent.type)) {
+ switch (parent.type) {
+
+ // e.g. foo.a = 0;
+ case "AssignmentExpression":
+ return parent.left === node;
+
+ // e.g. ++foo.a;
+ case "UpdateExpression":
+ return true;
+
+ // e.g. delete foo.a;
+ case "UnaryExpression":
+ if (parent.operator === "delete") {
+ return true;
+ }
+ break;
+
+ // EXCLUDES: e.g. cache.get(foo.a).b = 0;
+ case "CallExpression":
+ if (parent.callee !== node) {
+ return false;
+ }
+ break;
+
+ // EXCLUDES: e.g. cache[foo.a] = 0;
+ case "MemberExpression":
+ if (parent.property === node) {
+ return false;
+ }
+ break;
+
+ // EXCLUDES: e.g. ({ [foo]: a }) = bar;
+ case "Property":
+ if (parent.key === node) {
+ return false;
+ }
+
+ break;
+
+ // no default
+ }
+
+ node = parent;
+ parent = node.parent;
+ }
+
+ return false;
+ }
+
+ /**
+ * Reports a reference if is non initializer and writable.
+ * @param {Reference} reference - A reference to check.
+ * @param {int} index - The index of the reference in the references.
+ * @param {Reference[]} references - The array that the reference belongs to.
+ * @returns {void}
+ */
+ function checkReference(reference, index, references) {
+ const identifier = reference.identifier;
+
+ if (identifier &&
+ !reference.init &&
+
+ /*
+ * Destructuring assignments can have multiple default value,
+ * so possibly there are multiple writeable references for the same identifier.
+ */
+ (index === 0 || references[index - 1].identifier !== identifier)
+ ) {
+ if (reference.isWrite()) {
+ context.report({ node: identifier, message: "Assignment to function parameter '{{name}}'.", data: { name: identifier.name } });
+ } else if (props && isModifyingProp(reference) && ignoredPropertyAssignmentsFor.indexOf(identifier.name) === -1) {
+ context.report({ node: identifier, message: "Assignment to property of function parameter '{{name}}'.", data: { name: identifier.name } });
+ }
+ }
+ }
+
+ /**
+ * Finds and reports references that are non initializer and writable.
+ * @param {Variable} variable - A variable to check.
+ * @returns {void}
+ */
+ function checkVariable(variable) {
+ if (variable.defs[0].type === "Parameter") {
+ variable.references.forEach(checkReference);
+ }
+ }
+
+ /**
+ * Checks parameters of a given function node.
+ * @param {ASTNode} node - A function node to check.
+ * @returns {void}
+ */
+ function checkForFunction(node) {
+ context.getDeclaredVariables(node).forEach(checkVariable);
+ }
+
+ return {
+
+ // `:exit` is needed for the `node.parent` property of identifier nodes.
+ "FunctionDeclaration:exit": checkForFunction,
+ "FunctionExpression:exit": checkForFunction,
+ "ArrowFunctionExpression:exit": checkForFunction
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-path-concat.js b/tools/node_modules/eslint/lib/rules/no-path-concat.js
new file mode 100644
index 0000000000..1e153a43b6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-path-concat.js
@@ -0,0 +1,49 @@
+/**
+ * @fileoverview Disallow string concatenation when using __dirname and __filename
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow string concatenation with `__dirname` and `__filename`",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ const MATCHER = /^__(?:dir|file)name$/;
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ BinaryExpression(node) {
+
+ const left = node.left,
+ right = node.right;
+
+ if (node.operator === "+" &&
+ ((left.type === "Identifier" && MATCHER.test(left.name)) ||
+ (right.type === "Identifier" && MATCHER.test(right.name)))
+ ) {
+
+ context.report({ node, message: "Use path.join() or path.resolve() instead of + to create paths." });
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-plusplus.js b/tools/node_modules/eslint/lib/rules/no-plusplus.js
new file mode 100644
index 0000000000..94f259ac9e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-plusplus.js
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview Rule to flag use of unary increment and decrement operators.
+ * @author Ian Christian Myers
+ * @author Brody McKee (github.com/mrmckeb)
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the unary operators `++` and `--`",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowForLoopAfterthoughts: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const config = context.options[0];
+ let allowInForAfterthought = false;
+
+ if (typeof config === "object") {
+ allowInForAfterthought = config.allowForLoopAfterthoughts === true;
+ }
+
+ return {
+
+ UpdateExpression(node) {
+ if (allowInForAfterthought && node.parent.type === "ForStatement") {
+ return;
+ }
+ context.report({
+ node,
+ message: "Unary operator '{{operator}}' used.",
+ data: {
+ operator: node.operator
+ }
+ });
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-process-env.js b/tools/node_modules/eslint/lib/rules/no-process-env.js
new file mode 100644
index 0000000000..ef58b38e3c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-process-env.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Disallow the use of process.env()
+ * @author Vignesh Anand
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `process.env`",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ MemberExpression(node) {
+ const objectName = node.object.name,
+ propertyName = node.property.name;
+
+ if (objectName === "process" && !node.computed && propertyName && propertyName === "env") {
+ context.report({ node, message: "Unexpected use of process.env." });
+ }
+
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-process-exit.js b/tools/node_modules/eslint/lib/rules/no-process-exit.js
new file mode 100644
index 0000000000..04e423b88f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-process-exit.js
@@ -0,0 +1,35 @@
+/**
+ * @fileoverview Disallow the use of process.exit()
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `process.exit()`",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ "CallExpression > MemberExpression.callee[object.name = 'process'][property.name = 'exit']"(node) {
+ context.report({ node: node.parent, message: "Don't use process.exit(); throw an error instead." });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-proto.js b/tools/node_modules/eslint/lib/rules/no-proto.js
new file mode 100644
index 0000000000..933746f559
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-proto.js
@@ -0,0 +1,38 @@
+/**
+ * @fileoverview Rule to flag usage of __proto__ property
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of the `__proto__` property",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ MemberExpression(node) {
+
+ if (node.property &&
+ (node.property.type === "Identifier" && node.property.name === "__proto__" && !node.computed) ||
+ (node.property.type === "Literal" && node.property.value === "__proto__")) {
+ context.report({ node, message: "The '__proto__' property is deprecated." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-prototype-builtins.js b/tools/node_modules/eslint/lib/rules/no-prototype-builtins.js
new file mode 100644
index 0000000000..b9f040eaf6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-prototype-builtins.js
@@ -0,0 +1,54 @@
+/**
+ * @fileoverview Rule to disallow use of Object.prototype builtins on objects
+ * @author Andrew Levine
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow calling some `Object.prototype` methods directly on objects",
+ category: "Possible Errors",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const DISALLOWED_PROPS = [
+ "hasOwnProperty",
+ "isPrototypeOf",
+ "propertyIsEnumerable"
+ ];
+
+ /**
+ * Reports if a disallowed property is used in a CallExpression
+ * @param {ASTNode} node The CallExpression node.
+ * @returns {void}
+ */
+ function disallowBuiltIns(node) {
+ if (node.callee.type !== "MemberExpression" || node.callee.computed) {
+ return;
+ }
+ const propName = node.callee.property.name;
+
+ if (DISALLOWED_PROPS.indexOf(propName) > -1) {
+ context.report({
+ message: "Do not access Object.prototype method '{{prop}}' from target object.",
+ loc: node.callee.property.loc.start,
+ data: { prop: propName },
+ node
+ });
+ }
+ }
+
+ return {
+ CallExpression: disallowBuiltIns
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-redeclare.js b/tools/node_modules/eslint/lib/rules/no-redeclare.js
new file mode 100644
index 0000000000..ccb57003ed
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-redeclare.js
@@ -0,0 +1,101 @@
+/**
+ * @fileoverview Rule to flag when the same variable is declared more then once.
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow variable redeclaration",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ builtinGlobals: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = {
+ builtinGlobals: Boolean(context.options[0] && context.options[0].builtinGlobals)
+ };
+
+ /**
+ * Find variables in a given scope and flag redeclared ones.
+ * @param {Scope} scope - An eslint-scope scope object.
+ * @returns {void}
+ * @private
+ */
+ function findVariablesInScope(scope) {
+ scope.variables.forEach(variable => {
+ const hasBuiltin = options.builtinGlobals && "writeable" in variable;
+ const count = (hasBuiltin ? 1 : 0) + variable.identifiers.length;
+
+ if (count >= 2) {
+ variable.identifiers.sort((a, b) => a.range[1] - b.range[1]);
+
+ for (let i = (hasBuiltin ? 0 : 1), l = variable.identifiers.length; i < l; i++) {
+ context.report({ node: variable.identifiers[i], message: "'{{a}}' is already defined.", data: { a: variable.name } });
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Find variables in the current scope.
+ * @param {ASTNode} node - The Program node.
+ * @returns {void}
+ * @private
+ */
+ function checkForGlobal(node) {
+ const scope = context.getScope(),
+ parserOptions = context.parserOptions,
+ ecmaFeatures = parserOptions.ecmaFeatures || {};
+
+ // Nodejs env or modules has a special scope.
+ if (ecmaFeatures.globalReturn || node.sourceType === "module") {
+ findVariablesInScope(scope.childScopes[0]);
+ } else {
+ findVariablesInScope(scope);
+ }
+ }
+
+ /**
+ * Find variables in the current scope.
+ * @returns {void}
+ * @private
+ */
+ function checkForBlock() {
+ findVariablesInScope(context.getScope());
+ }
+
+ if (context.parserOptions.ecmaVersion >= 6) {
+ return {
+ Program: checkForGlobal,
+ BlockStatement: checkForBlock,
+ SwitchStatement: checkForBlock
+ };
+ }
+ return {
+ Program: checkForGlobal,
+ FunctionDeclaration: checkForBlock,
+ FunctionExpression: checkForBlock,
+ ArrowFunctionExpression: checkForBlock
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-regex-spaces.js b/tools/node_modules/eslint/lib/rules/no-regex-spaces.js
new file mode 100644
index 0000000000..9250437caa
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-regex-spaces.js
@@ -0,0 +1,114 @@
+/**
+ * @fileoverview Rule to count multiple spaces in regular expressions
+ * @author Matt DuVall <http://www.mattduvall.com/>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow multiple spaces in regular expressions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Validate regular expressions
+ * @param {ASTNode} node node to validate
+ * @param {string} value regular expression to validate
+ * @param {number} valueStart The start location of the regex/string literal. It will always be the case that
+ * `sourceCode.getText().slice(valueStart, valueStart + value.length) === value`
+ * @returns {void}
+ * @private
+ */
+ function checkRegex(node, value, valueStart) {
+ const multipleSpacesRegex = /( {2,})( [+*{?]|[^+*{?]|$)/,
+ regexResults = multipleSpacesRegex.exec(value);
+
+ if (regexResults !== null) {
+ const count = regexResults[1].length;
+
+ context.report({
+ node,
+ message: "Spaces are hard to count. Use {{{count}}}.",
+ data: { count },
+ fix(fixer) {
+ return fixer.replaceTextRange(
+ [valueStart + regexResults.index, valueStart + regexResults.index + count],
+ ` {${count}}`
+ );
+ }
+ });
+
+ /*
+ * TODO: (platinumazure) Fix message to use rule message
+ * substitution when api.report is fixed in lib/eslint.js.
+ */
+ }
+ }
+
+ /**
+ * Validate regular expression literals
+ * @param {ASTNode} node node to validate
+ * @returns {void}
+ * @private
+ */
+ function checkLiteral(node) {
+ const token = sourceCode.getFirstToken(node),
+ nodeType = token.type,
+ nodeValue = token.value;
+
+ if (nodeType === "RegularExpression") {
+ checkRegex(node, nodeValue, token.range[0]);
+ }
+ }
+
+ /**
+ * Check if node is a string
+ * @param {ASTNode} node node to evaluate
+ * @returns {boolean} True if its a string
+ * @private
+ */
+ function isString(node) {
+ return node && node.type === "Literal" && typeof node.value === "string";
+ }
+
+ /**
+ * Validate strings passed to the RegExp constructor
+ * @param {ASTNode} node node to validate
+ * @returns {void}
+ * @private
+ */
+ function checkFunction(node) {
+ const scope = context.getScope();
+ const regExpVar = astUtils.getVariableByName(scope, "RegExp");
+ const shadowed = regExpVar && regExpVar.defs.length > 0;
+
+ if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0]) && !shadowed) {
+ checkRegex(node, node.arguments[0].value, node.arguments[0].range[0] + 1);
+ }
+ }
+
+ return {
+ Literal: checkLiteral,
+ CallExpression: checkFunction,
+ NewExpression: checkFunction
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-restricted-globals.js b/tools/node_modules/eslint/lib/rules/no-restricted-globals.js
new file mode 100644
index 0000000000..75428fc174
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-restricted-globals.js
@@ -0,0 +1,120 @@
+/**
+ * @fileoverview Restrict usage of specified globals.
+ * @author Benoît Zugmeyer
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const DEFAULT_MESSAGE_TEMPLATE = "Unexpected use of '{{name}}'.",
+ CUSTOM_MESSAGE_TEMPLATE = "Unexpected use of '{{name}}'. {{customMessage}}";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow specified global variables",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: {
+ type: "array",
+ items: {
+ oneOf: [
+ {
+ type: "string"
+ },
+ {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ message: { type: "string" }
+ },
+ required: ["name"],
+ additionalProperties: false
+ }
+ ]
+ },
+ uniqueItems: true,
+ minItems: 0
+ }
+ },
+
+ create(context) {
+
+ // If no globals are restricted, we don't need to do anything
+ if (context.options.length === 0) {
+ return {};
+ }
+
+ const restrictedGlobalMessages = context.options.reduce((memo, option) => {
+ if (typeof option === "string") {
+ memo[option] = null;
+ } else {
+ memo[option.name] = option.message;
+ }
+
+ return memo;
+ }, {});
+
+ /**
+ * Report a variable to be used as a restricted global.
+ * @param {Reference} reference the variable reference
+ * @returns {void}
+ * @private
+ */
+ function reportReference(reference) {
+ const name = reference.identifier.name,
+ customMessage = restrictedGlobalMessages[name],
+ message = customMessage
+ ? CUSTOM_MESSAGE_TEMPLATE
+ : DEFAULT_MESSAGE_TEMPLATE;
+
+ context.report({
+ node: reference.identifier,
+ message,
+ data: {
+ name,
+ customMessage
+ }
+ });
+ }
+
+ /**
+ * Check if the given name is a restricted global name.
+ * @param {string} name name of a variable
+ * @returns {boolean} whether the variable is a restricted global or not
+ * @private
+ */
+ function isRestricted(name) {
+ return restrictedGlobalMessages.hasOwnProperty(name);
+ }
+
+ return {
+ Program() {
+ const scope = context.getScope();
+
+ // Report variables declared elsewhere (ex: variables defined as "global" by eslint)
+ scope.variables.forEach(variable => {
+ if (!variable.defs.length && isRestricted(variable.name)) {
+ variable.references.forEach(reportReference);
+ }
+ });
+
+ // Report variables not declared at all
+ scope.through.forEach(reference => {
+ if (isRestricted(reference.identifier.name)) {
+ reportReference(reference);
+ }
+ });
+
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-restricted-imports.js b/tools/node_modules/eslint/lib/rules/no-restricted-imports.js
new file mode 100644
index 0000000000..eb477b4be6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-restricted-imports.js
@@ -0,0 +1,263 @@
+/**
+ * @fileoverview Restrict usage of specified node imports.
+ * @author Guy Ellis
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const DEFAULT_MESSAGE_TEMPLATE = "'{{importSource}}' import is restricted from being used.";
+const CUSTOM_MESSAGE_TEMPLATE = "'{{importSource}}' import is restricted from being used. {{customMessage}}";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const ignore = require("ignore");
+
+const arrayOfStrings = {
+ type: "array",
+ items: { type: "string" },
+ uniqueItems: true
+};
+
+const arrayOfStringsOrObjects = {
+ type: "array",
+ items: {
+ anyOf: [
+ { type: "string" },
+ {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ message: {
+ type: "string",
+ minLength: 1
+ },
+ importNames: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ }
+ },
+ additionalProperties: false,
+ required: ["name"]
+ }
+ ]
+ },
+ uniqueItems: true
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow specified modules when loaded by `import`",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ arrayOfStringsOrObjects,
+ {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ paths: arrayOfStringsOrObjects,
+ patterns: arrayOfStrings
+ },
+ additionalProperties: false
+ },
+ additionalItems: false
+ }
+ ]
+ }
+ },
+
+ create(context) {
+ const options = Array.isArray(context.options) ? context.options : [];
+ const isPathAndPatternsObject =
+ typeof options[0] === "object" &&
+ (options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
+
+ const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
+ const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
+
+ const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
+ if (typeof importSource === "string") {
+ memo[importSource] = { message: null };
+ } else {
+ memo[importSource.name] = {
+ message: importSource.message,
+ importNames: importSource.importNames
+ };
+ }
+ return memo;
+ }, {});
+
+ // if no imports are restricted we don"t need to check
+ if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
+ return {};
+ }
+
+ const restrictedPatternsMatcher = ignore().add(restrictedPatterns);
+
+ /**
+ * Checks to see if "*" is being used to import everything.
+ * @param {Set.<string>} importNames - Set of import names that are being imported
+ * @returns {boolean} whether everything is imported or not
+ */
+ function isEverythingImported(importNames) {
+ return importNames.has("*");
+ }
+
+ /**
+ * Report a restricted path.
+ * @param {node} node representing the restricted path reference
+ * @returns {void}
+ * @private
+ */
+ function reportPath(node) {
+ const importSource = node.source.value.trim();
+ const customMessage = restrictedPathMessages[importSource] && restrictedPathMessages[importSource].message;
+ const message = customMessage
+ ? CUSTOM_MESSAGE_TEMPLATE
+ : DEFAULT_MESSAGE_TEMPLATE;
+
+ context.report({
+ node,
+ message,
+ data: {
+ importSource,
+ customMessage
+ }
+ });
+ }
+
+ /**
+ * Report a restricted path specifically for patterns.
+ * @param {node} node - representing the restricted path reference
+ * @returns {void}
+ * @private
+ */
+ function reportPathForPatterns(node) {
+ const importSource = node.source.value.trim();
+
+ context.report({
+ node,
+ message: "'{{importSource}}' import is restricted from being used by a pattern.",
+ data: {
+ importSource
+ }
+ });
+ }
+
+ /**
+ * Report a restricted path specifically when using the '*' import.
+ * @param {string} importSource - path of the import
+ * @param {node} node - representing the restricted path reference
+ * @returns {void}
+ * @private
+ */
+ function reportPathForEverythingImported(importSource, node) {
+ const importNames = restrictedPathMessages[importSource].importNames;
+
+ context.report({
+ node,
+ message: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
+ data: {
+ importSource,
+ importNames
+ }
+ });
+ }
+
+ /**
+ * Check if the given importSource is restricted because '*' is being imported.
+ * @param {string} importSource - path of the import
+ * @param {Set.<string>} importNames - Set of import names that are being imported
+ * @returns {boolean} whether the path is restricted
+ * @private
+ */
+ function isRestrictedForEverythingImported(importSource, importNames) {
+ return Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource) &&
+ restrictedPathMessages[importSource].importNames &&
+ isEverythingImported(importNames);
+ }
+
+ /**
+ * Check if the given importNames are restricted given a list of restrictedImportNames.
+ * @param {Set.<string>} importNames - Set of import names that are being imported
+ * @param {[string]} restrictedImportNames - array of import names that are restricted for this import
+ * @returns {boolean} whether the objectName is restricted
+ * @private
+ */
+ function isRestrictedObject(importNames, restrictedImportNames) {
+ return restrictedImportNames.some(restrictedObjectName => (
+ importNames.has(restrictedObjectName)
+ ));
+ }
+
+ /**
+ * Check if the given importSource is a restricted path.
+ * @param {string} importSource - path of the import
+ * @param {Set.<string>} importNames - Set of import names that are being imported
+ * @returns {boolean} whether the variable is a restricted path or not
+ * @private
+ */
+ function isRestrictedPath(importSource, importNames) {
+ let isRestricted = false;
+
+ if (Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
+ if (restrictedPathMessages[importSource].importNames) {
+ isRestricted = isRestrictedObject(importNames, restrictedPathMessages[importSource].importNames);
+ } else {
+ isRestricted = true;
+ }
+ }
+
+ return isRestricted;
+ }
+
+ /**
+ * Check if the given importSource is restricted by a pattern.
+ * @param {string} importSource - path of the import
+ * @returns {boolean} whether the variable is a restricted pattern or not
+ * @private
+ */
+ function isRestrictedPattern(importSource) {
+ return restrictedPatterns.length > 0 && restrictedPatternsMatcher.ignores(importSource);
+ }
+
+ return {
+ ImportDeclaration(node) {
+ const importSource = node.source.value.trim();
+ const importNames = node.specifiers.reduce((set, specifier) => {
+ if (specifier.type === "ImportDefaultSpecifier") {
+ set.add("default");
+ } else if (specifier.type === "ImportNamespaceSpecifier") {
+ set.add("*");
+ } else {
+ set.add(specifier.imported.name);
+ }
+ return set;
+ }, new Set());
+
+ if (isRestrictedForEverythingImported(importSource, importNames)) {
+ reportPathForEverythingImported(importSource, node);
+ }
+
+ if (isRestrictedPath(importSource, importNames)) {
+ reportPath(node);
+ }
+ if (isRestrictedPattern(importSource)) {
+ reportPathForPatterns(node);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-restricted-modules.js b/tools/node_modules/eslint/lib/rules/no-restricted-modules.js
new file mode 100644
index 0000000000..cd47975733
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-restricted-modules.js
@@ -0,0 +1,177 @@
+/**
+ * @fileoverview Restrict usage of specified node modules.
+ * @author Christian Schulz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const DEFAULT_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used.";
+const CUSTOM_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used. {{customMessage}}";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const ignore = require("ignore");
+
+const arrayOfStrings = {
+ type: "array",
+ items: { type: "string" },
+ uniqueItems: true
+};
+
+const arrayOfStringsOrObjects = {
+ type: "array",
+ items: {
+ anyOf: [
+ { type: "string" },
+ {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ message: {
+ type: "string",
+ minLength: 1
+ }
+ },
+ additionalProperties: false,
+ required: ["name"]
+ }
+ ]
+ },
+ uniqueItems: true
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow specified modules when loaded by `require`",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ arrayOfStringsOrObjects,
+ {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ paths: arrayOfStringsOrObjects,
+ patterns: arrayOfStrings
+ },
+ additionalProperties: false
+ },
+ additionalItems: false
+ }
+ ]
+ }
+ },
+
+ create(context) {
+ const options = Array.isArray(context.options) ? context.options : [];
+ const isPathAndPatternsObject =
+ typeof options[0] === "object" &&
+ (options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
+
+ const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
+ const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
+
+ const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
+ if (typeof importName === "string") {
+ memo[importName] = null;
+ } else {
+ memo[importName.name] = importName.message;
+ }
+ return memo;
+ }, {});
+
+ // if no imports are restricted we don"t need to check
+ if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
+ return {};
+ }
+
+ const ig = ignore().add(restrictedPatterns);
+
+
+ /**
+ * Function to check if a node is a string literal.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} If the node is a string literal.
+ */
+ function isString(node) {
+ return node && node.type === "Literal" && typeof node.value === "string";
+ }
+
+ /**
+ * Function to check if a node is a require call.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} If the node is a require call.
+ */
+ function isRequireCall(node) {
+ return node.callee.type === "Identifier" && node.callee.name === "require";
+ }
+
+ /**
+ * Report a restricted path.
+ * @param {node} node representing the restricted path reference
+ * @returns {void}
+ * @private
+ */
+ function reportPath(node) {
+ const moduleName = node.arguments[0].value.trim();
+ const customMessage = restrictedPathMessages[moduleName];
+ const message = customMessage
+ ? CUSTOM_MESSAGE_TEMPLATE
+ : DEFAULT_MESSAGE_TEMPLATE;
+
+ context.report({
+ node,
+ message,
+ data: {
+ moduleName,
+ customMessage
+ }
+ });
+ }
+
+ /**
+ * Check if the given name is a restricted path name
+ * @param {string} name name of a variable
+ * @returns {boolean} whether the variable is a restricted path or not
+ * @private
+ */
+ function isRestrictedPath(name) {
+ return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
+ }
+
+ return {
+ CallExpression(node) {
+ if (isRequireCall(node)) {
+
+ // node has arguments and first argument is string
+ if (node.arguments.length && isString(node.arguments[0])) {
+ const moduleName = node.arguments[0].value.trim();
+
+ // check if argument value is in restricted modules array
+ if (isRestrictedPath(moduleName)) {
+ reportPath(node);
+ }
+
+ if (restrictedPatterns.length > 0 && ig.ignores(moduleName)) {
+ context.report({
+ node,
+ message: "'{{moduleName}}' module is restricted from being used by a pattern.",
+ data: { moduleName }
+ });
+ }
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-restricted-properties.js b/tools/node_modules/eslint/lib/rules/no-restricted-properties.js
new file mode 100644
index 0000000000..cdc73f9e41
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-restricted-properties.js
@@ -0,0 +1,173 @@
+/**
+ * @fileoverview Rule to disallow certain object properties
+ * @author Will Klein & Eli White
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow certain properties on certain objects",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: {
+ type: "array",
+ items: {
+ anyOf: [ // `object` and `property` are both optional, but at least one of them must be provided.
+ {
+ type: "object",
+ properties: {
+ object: {
+ type: "string"
+ },
+ property: {
+ type: "string"
+ },
+ message: {
+ type: "string"
+ }
+ },
+ additionalProperties: false,
+ required: ["object"]
+ },
+ {
+ type: "object",
+ properties: {
+ object: {
+ type: "string"
+ },
+ property: {
+ type: "string"
+ },
+ message: {
+ type: "string"
+ }
+ },
+ additionalProperties: false,
+ required: ["property"]
+ }
+ ]
+ },
+ uniqueItems: true
+ }
+ },
+
+ create(context) {
+ const restrictedCalls = context.options;
+
+ if (restrictedCalls.length === 0) {
+ return {};
+ }
+
+ const restrictedProperties = new Map();
+ const globallyRestrictedObjects = new Map();
+ const globallyRestrictedProperties = new Map();
+
+ restrictedCalls.forEach(option => {
+ const objectName = option.object;
+ const propertyName = option.property;
+
+ if (typeof objectName === "undefined") {
+ globallyRestrictedProperties.set(propertyName, { message: option.message });
+ } else if (typeof propertyName === "undefined") {
+ globallyRestrictedObjects.set(objectName, { message: option.message });
+ } else {
+ if (!restrictedProperties.has(objectName)) {
+ restrictedProperties.set(objectName, new Map());
+ }
+
+ restrictedProperties.get(objectName).set(propertyName, {
+ message: option.message
+ });
+ }
+ });
+
+ /**
+ * Checks to see whether a property access is restricted, and reports it if so.
+ * @param {ASTNode} node The node to report
+ * @param {string} objectName The name of the object
+ * @param {string} propertyName The name of the property
+ * @returns {undefined}
+ */
+ function checkPropertyAccess(node, objectName, propertyName) {
+ if (propertyName === null) {
+ return;
+ }
+ const matchedObject = restrictedProperties.get(objectName);
+ const matchedObjectProperty = matchedObject ? matchedObject.get(propertyName) : globallyRestrictedObjects.get(objectName);
+ const globalMatchedProperty = globallyRestrictedProperties.get(propertyName);
+
+ if (matchedObjectProperty) {
+ const message = matchedObjectProperty.message ? ` ${matchedObjectProperty.message}` : "";
+
+ context.report({
+ node,
+ // eslint-disable-next-line eslint-plugin/report-message-format
+ message: "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}",
+ data: {
+ objectName,
+ propertyName,
+ message
+ }
+ });
+ } else if (globalMatchedProperty) {
+ const message = globalMatchedProperty.message ? ` ${globalMatchedProperty.message}` : "";
+
+ context.report({
+ node,
+ // eslint-disable-next-line eslint-plugin/report-message-format
+ message: "'{{propertyName}}' is restricted from being used.{{message}}",
+ data: {
+ propertyName,
+ message
+ }
+ });
+ }
+ }
+
+ /**
+ * Checks property accesses in a destructuring assignment expression, e.g. `var foo; ({foo} = bar);`
+ * @param {ASTNode} node An AssignmentExpression or AssignmentPattern node
+ * @returns {undefined}
+ */
+ function checkDestructuringAssignment(node) {
+ if (node.right.type === "Identifier") {
+ const objectName = node.right.name;
+
+ if (node.left.type === "ObjectPattern") {
+ node.left.properties.forEach(property => {
+ checkPropertyAccess(node.left, objectName, astUtils.getStaticPropertyName(property));
+ });
+ }
+ }
+ }
+
+ return {
+ MemberExpression(node) {
+ checkPropertyAccess(node, node.object && node.object.name, astUtils.getStaticPropertyName(node));
+ },
+ VariableDeclarator(node) {
+ if (node.init && node.init.type === "Identifier") {
+ const objectName = node.init.name;
+
+ if (node.id.type === "ObjectPattern") {
+ node.id.properties.forEach(property => {
+ checkPropertyAccess(node.id, objectName, astUtils.getStaticPropertyName(property));
+ });
+ }
+ }
+ },
+ AssignmentExpression: checkDestructuringAssignment,
+ AssignmentPattern: checkDestructuringAssignment
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-restricted-syntax.js b/tools/node_modules/eslint/lib/rules/no-restricted-syntax.js
new file mode 100644
index 0000000000..1798065ec0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-restricted-syntax.js
@@ -0,0 +1,62 @@
+/**
+ * @fileoverview Rule to flag use of certain node types
+ * @author Burak Yigit Kaya
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow specified syntax",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: {
+ type: "array",
+ items: [{
+ oneOf: [
+ {
+ type: "string"
+ },
+ {
+ type: "object",
+ properties: {
+ selector: { type: "string" },
+ message: { type: "string" }
+ },
+ required: ["selector"],
+ additionalProperties: false
+ }
+ ]
+ }],
+ uniqueItems: true,
+ minItems: 0
+ }
+ },
+
+ create(context) {
+ return context.options.reduce((result, selectorOrObject) => {
+ const isStringFormat = (typeof selectorOrObject === "string");
+ const hasCustomMessage = !isStringFormat && Boolean(selectorOrObject.message);
+
+ const selector = isStringFormat ? selectorOrObject : selectorOrObject.selector;
+ const message = hasCustomMessage ? selectorOrObject.message : "Using '{{selector}}' is not allowed.";
+
+ return Object.assign(result, {
+ [selector](node) {
+ context.report({
+ node,
+ message,
+ data: hasCustomMessage ? {} : { selector }
+ });
+ }
+ });
+ }, {});
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-return-assign.js b/tools/node_modules/eslint/lib/rules/no-return-assign.js
new file mode 100644
index 0000000000..882f94b724
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-return-assign.js
@@ -0,0 +1,71 @@
+/**
+ * @fileoverview Rule to flag when return statement contains assignment
+ * @author Ilya Volodin
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const SENTINEL_TYPE = /^(?:[a-zA-Z]+?Statement|ArrowFunctionExpression|FunctionExpression|ClassExpression)$/;
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow assignment operators in `return` statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["except-parens", "always"]
+ }
+ ]
+ },
+
+ create(context) {
+ const always = (context.options[0] || "except-parens") !== "except-parens";
+ const sourceCode = context.getSourceCode();
+
+ return {
+ AssignmentExpression(node) {
+ if (!always && astUtils.isParenthesised(sourceCode, node)) {
+ return;
+ }
+
+ let parent = node.parent;
+
+ // Find ReturnStatement or ArrowFunctionExpression in ancestors.
+ while (parent && !SENTINEL_TYPE.test(parent.type)) {
+ node = parent;
+ parent = parent.parent;
+ }
+
+ // Reports.
+ if (parent && parent.type === "ReturnStatement") {
+ context.report({
+ node: parent,
+ message: "Return statement should not contain assignment."
+ });
+ } else if (parent && parent.type === "ArrowFunctionExpression" && parent.body === node) {
+ context.report({
+ node: parent,
+ message: "Arrow function should not return assignment."
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-return-await.js b/tools/node_modules/eslint/lib/rules/no-return-await.js
new file mode 100644
index 0000000000..2f06b61108
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-return-await.js
@@ -0,0 +1,94 @@
+/**
+ * @fileoverview Disallows unnecessary `return await`
+ * @author Jordan Harband
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const message = "Redundant use of `await` on a return value.";
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary `return await`",
+ category: "Best Practices",
+ recommended: false // TODO: set to true
+ },
+ fixable: null,
+ schema: [
+ ]
+ },
+
+ create(context) {
+
+ /**
+ * Reports a found unnecessary `await` expression.
+ * @param {ASTNode} node The node representing the `await` expression to report
+ * @returns {void}
+ */
+ function reportUnnecessaryAwait(node) {
+ context.report({
+ node: context.getSourceCode().getFirstToken(node),
+ loc: node.loc,
+ message
+ });
+ }
+
+ /**
+ * Determines whether a thrown error from this node will be caught/handled within this function rather than immediately halting
+ * this function. For example, a statement in a `try` block will always have an error handler. A statement in
+ * a `catch` block will only have an error handler if there is also a `finally` block.
+ * @param {ASTNode} node A node representing a location where an could be thrown
+ * @returns {boolean} `true` if a thrown error will be caught/handled in this function
+ */
+ function hasErrorHandler(node) {
+ let ancestor = node;
+
+ while (!astUtils.isFunction(ancestor) && ancestor.type !== "Program") {
+ if (ancestor.parent.type === "TryStatement" && (ancestor === ancestor.parent.block || ancestor === ancestor.parent.handler && ancestor.parent.finalizer)) {
+ return true;
+ }
+ ancestor = ancestor.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if a node is placed in tail call position. Once `return` arguments (or arrow function expressions) can be a complex expression,
+ * an `await` expression could or could not be unnecessary by the definition of this rule. So we're looking for `await` expressions that are in tail position.
+ * @param {ASTNode} node A node representing the `await` expression to check
+ * @returns {boolean} The checking result
+ */
+ function isInTailCallPosition(node) {
+ if (node.parent.type === "ArrowFunctionExpression") {
+ return true;
+ }
+ if (node.parent.type === "ReturnStatement") {
+ return !hasErrorHandler(node.parent);
+ }
+ if (node.parent.type === "ConditionalExpression" && (node === node.parent.consequent || node === node.parent.alternate)) {
+ return isInTailCallPosition(node.parent);
+ }
+ if (node.parent.type === "LogicalExpression" && node === node.parent.right) {
+ return isInTailCallPosition(node.parent);
+ }
+ if (node.parent.type === "SequenceExpression" && node === node.parent.expressions[node.parent.expressions.length - 1]) {
+ return isInTailCallPosition(node.parent);
+ }
+ return false;
+ }
+
+ return {
+ AwaitExpression(node) {
+ if (isInTailCallPosition(node) && !hasErrorHandler(node)) {
+ reportUnnecessaryAwait(node);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-script-url.js b/tools/node_modules/eslint/lib/rules/no-script-url.js
new file mode 100644
index 0000000000..98f988ff1a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-script-url.js
@@ -0,0 +1,41 @@
+/**
+ * @fileoverview Rule to flag when using javascript: urls
+ * @author Ilya Volodin
+ */
+/* jshint scripturl: true */
+/* eslint no-script-url: 0 */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `javascript:` urls",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ Literal(node) {
+ if (node.value && typeof node.value === "string") {
+ const value = node.value.toLowerCase();
+
+ if (value.indexOf("javascript:") === 0) {
+ context.report({ node, message: "Script URL is a form of eval." });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-self-assign.js b/tools/node_modules/eslint/lib/rules/no-self-assign.js
new file mode 100644
index 0000000000..48b922d46b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-self-assign.js
@@ -0,0 +1,214 @@
+/**
+ * @fileoverview Rule to disallow assignments where both sides are exactly the same
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const SPACES = /\s+/g;
+
+/**
+ * Checks whether the property of 2 given member expression nodes are the same
+ * property or not.
+ *
+ * @param {ASTNode} left - A member expression node to check.
+ * @param {ASTNode} right - Another member expression node to check.
+ * @returns {boolean} `true` if the member expressions have the same property.
+ */
+function isSameProperty(left, right) {
+ if (left.property.type === "Identifier" &&
+ left.property.type === right.property.type &&
+ left.property.name === right.property.name &&
+ left.computed === right.computed
+ ) {
+ return true;
+ }
+
+ const lname = astUtils.getStaticPropertyName(left);
+ const rname = astUtils.getStaticPropertyName(right);
+
+ return lname !== null && lname === rname;
+}
+
+/**
+ * Checks whether 2 given member expression nodes are the reference to the same
+ * property or not.
+ *
+ * @param {ASTNode} left - A member expression node to check.
+ * @param {ASTNode} right - Another member expression node to check.
+ * @returns {boolean} `true` if the member expressions are the reference to the
+ * same property or not.
+ */
+function isSameMember(left, right) {
+ if (!isSameProperty(left, right)) {
+ return false;
+ }
+
+ const lobj = left.object;
+ const robj = right.object;
+
+ if (lobj.type !== robj.type) {
+ return false;
+ }
+ if (lobj.type === "MemberExpression") {
+ return isSameMember(lobj, robj);
+ }
+ return lobj.type === "Identifier" && lobj.name === robj.name;
+}
+
+/**
+ * Traverses 2 Pattern nodes in parallel, then reports self-assignments.
+ *
+ * @param {ASTNode|null} left - A left node to traverse. This is a Pattern or
+ * a Property.
+ * @param {ASTNode|null} right - A right node to traverse. This is a Pattern or
+ * a Property.
+ * @param {boolean} props - The flag to check member expressions as well.
+ * @param {Function} report - A callback function to report.
+ * @returns {void}
+ */
+function eachSelfAssignment(left, right, props, report) {
+ if (!left || !right) {
+
+ // do nothing
+ } else if (
+ left.type === "Identifier" &&
+ right.type === "Identifier" &&
+ left.name === right.name
+ ) {
+ report(right);
+ } else if (
+ left.type === "ArrayPattern" &&
+ right.type === "ArrayExpression"
+ ) {
+ const end = Math.min(left.elements.length, right.elements.length);
+
+ for (let i = 0; i < end; ++i) {
+ const rightElement = right.elements[i];
+
+ eachSelfAssignment(left.elements[i], rightElement, props, report);
+
+ // After a spread element, those indices are unknown.
+ if (rightElement && rightElement.type === "SpreadElement") {
+ break;
+ }
+ }
+ } else if (
+ left.type === "RestElement" &&
+ right.type === "SpreadElement"
+ ) {
+ eachSelfAssignment(left.argument, right.argument, props, report);
+ } else if (
+ left.type === "ObjectPattern" &&
+ right.type === "ObjectExpression" &&
+ right.properties.length >= 1
+ ) {
+
+ /*
+ * Gets the index of the last spread property.
+ * It's possible to overwrite properties followed by it.
+ */
+ let startJ = 0;
+
+ for (let i = right.properties.length - 1; i >= 0; --i) {
+ if (right.properties[i].type === "ExperimentalSpreadProperty") {
+ startJ = i + 1;
+ break;
+ }
+ }
+
+ for (let i = 0; i < left.properties.length; ++i) {
+ for (let j = startJ; j < right.properties.length; ++j) {
+ eachSelfAssignment(
+ left.properties[i],
+ right.properties[j],
+ props,
+ report
+ );
+ }
+ }
+ } else if (
+ left.type === "Property" &&
+ right.type === "Property" &&
+ !left.computed &&
+ !right.computed &&
+ right.kind === "init" &&
+ !right.method &&
+ left.key.name === right.key.name
+ ) {
+ eachSelfAssignment(left.value, right.value, props, report);
+ } else if (
+ props &&
+ left.type === "MemberExpression" &&
+ right.type === "MemberExpression" &&
+ isSameMember(left, right)
+ ) {
+ report(right);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow assignments where both sides are exactly the same",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ props: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const options = context.options[0];
+ const props = Boolean(options && options.props);
+
+ /**
+ * Reports a given node as self assignments.
+ *
+ * @param {ASTNode} node - A node to report. This is an Identifier node.
+ * @returns {void}
+ */
+ function report(node) {
+ context.report({
+ node,
+ message: "'{{name}}' is assigned to itself.",
+ data: {
+ name: sourceCode.getText(node).replace(SPACES, "")
+ }
+ });
+ }
+
+ return {
+ AssignmentExpression(node) {
+ if (node.operator === "=") {
+ eachSelfAssignment(node.left, node.right, props, report);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-self-compare.js b/tools/node_modules/eslint/lib/rules/no-self-compare.js
new file mode 100644
index 0000000000..5beaa181b9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-self-compare.js
@@ -0,0 +1,53 @@
+/**
+ * @fileoverview Rule to flag comparison where left part is the same as the right
+ * part.
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow comparisons where both sides are exactly the same",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether two nodes are composed of the same tokens.
+ * @param {ASTNode} nodeA The first node
+ * @param {ASTNode} nodeB The second node
+ * @returns {boolean} true if the nodes have identical token representations
+ */
+ function hasSameTokens(nodeA, nodeB) {
+ const tokensA = sourceCode.getTokens(nodeA);
+ const tokensB = sourceCode.getTokens(nodeB);
+
+ return tokensA.length === tokensB.length &&
+ tokensA.every((token, index) => token.type === tokensB[index].type && token.value === tokensB[index].value);
+ }
+
+ return {
+
+ BinaryExpression(node) {
+ const operators = new Set(["===", "==", "!==", "!=", ">", "<", ">=", "<="]);
+
+ if (operators.has(node.operator) && hasSameTokens(node.left, node.right)) {
+ context.report({ node, message: "Comparing to itself is potentially pointless." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-sequences.js b/tools/node_modules/eslint/lib/rules/no-sequences.js
new file mode 100644
index 0000000000..5e746dfa88
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-sequences.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Rule to flag use of comma operator
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow comma operators",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Parts of the grammar that are required to have parens.
+ */
+ const parenthesized = {
+ DoWhileStatement: "test",
+ IfStatement: "test",
+ SwitchStatement: "discriminant",
+ WhileStatement: "test",
+ WithStatement: "object",
+ ArrowFunctionExpression: "body"
+
+ /*
+ * Omitting CallExpression - commas are parsed as argument separators
+ * Omitting NewExpression - commas are parsed as argument separators
+ * Omitting ForInStatement - parts aren't individually parenthesised
+ * Omitting ForStatement - parts aren't individually parenthesised
+ */
+ };
+
+ /**
+ * Determines whether a node is required by the grammar to be wrapped in
+ * parens, e.g. the test of an if statement.
+ * @param {ASTNode} node - The AST node
+ * @returns {boolean} True if parens around node belong to parent node.
+ */
+ function requiresExtraParens(node) {
+ return node.parent && parenthesized[node.parent.type] &&
+ node === node.parent[parenthesized[node.parent.type]];
+ }
+
+ /**
+ * Check if a node is wrapped in parens.
+ * @param {ASTNode} node - The AST node
+ * @returns {boolean} True if the node has a paren on each side.
+ */
+ function isParenthesised(node) {
+ return astUtils.isParenthesised(sourceCode, node);
+ }
+
+ /**
+ * Check if a node is wrapped in two levels of parens.
+ * @param {ASTNode} node - The AST node
+ * @returns {boolean} True if two parens surround the node on each side.
+ */
+ function isParenthesisedTwice(node) {
+ const previousToken = sourceCode.getTokenBefore(node, 1),
+ nextToken = sourceCode.getTokenAfter(node, 1);
+
+ return isParenthesised(node) && previousToken && nextToken &&
+ astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
+ astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
+ }
+
+ return {
+ SequenceExpression(node) {
+
+ // Always allow sequences in for statement update
+ if (node.parent.type === "ForStatement" &&
+ (node === node.parent.init || node === node.parent.update)) {
+ return;
+ }
+
+ // Wrapping a sequence in extra parens indicates intent
+ if (requiresExtraParens(node)) {
+ if (isParenthesisedTwice(node)) {
+ return;
+ }
+ } else {
+ if (isParenthesised(node)) {
+ return;
+ }
+ }
+
+ const child = sourceCode.getTokenAfter(node.expressions[0]);
+
+ context.report({ node, loc: child.loc.start, message: "Unexpected use of comma operator." });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-shadow-restricted-names.js b/tools/node_modules/eslint/lib/rules/no-shadow-restricted-names.js
new file mode 100644
index 0000000000..6c60232b8b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-shadow-restricted-names.js
@@ -0,0 +1,69 @@
+/**
+ * @fileoverview Disallow shadowing of NaN, undefined, and Infinity (ES5 section 15.1.1)
+ * @author Michael Ficarra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow identifiers from shadowing restricted names",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ const RESTRICTED = ["undefined", "NaN", "Infinity", "arguments", "eval"];
+
+ /**
+ * Check if the node name is present inside the restricted list
+ * @param {ASTNode} id id to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkForViolation(id) {
+ if (RESTRICTED.indexOf(id.name) > -1) {
+ context.report({
+ node: id,
+ message: "Shadowing of global property '{{idName}}'.",
+ data: {
+ idName: id.name
+ }
+ });
+ }
+ }
+
+ return {
+ VariableDeclarator(node) {
+ checkForViolation(node.id);
+ },
+ ArrowFunctionExpression(node) {
+ [].map.call(node.params, checkForViolation);
+ },
+ FunctionExpression(node) {
+ if (node.id) {
+ checkForViolation(node.id);
+ }
+ [].map.call(node.params, checkForViolation);
+ },
+ FunctionDeclaration(node) {
+ if (node.id) {
+ checkForViolation(node.id);
+ [].map.call(node.params, checkForViolation);
+ }
+ },
+ CatchClause(node) {
+ checkForViolation(node.param);
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-shadow.js b/tools/node_modules/eslint/lib/rules/no-shadow.js
new file mode 100644
index 0000000000..e093d48c81
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-shadow.js
@@ -0,0 +1,188 @@
+/**
+ * @fileoverview Rule to flag on declaring variables already declared in the outer scope
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow variable declarations from shadowing variables declared in the outer scope",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ builtinGlobals: { type: "boolean" },
+ hoist: { enum: ["all", "functions", "never"] },
+ allow: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = {
+ builtinGlobals: Boolean(context.options[0] && context.options[0].builtinGlobals),
+ hoist: (context.options[0] && context.options[0].hoist) || "functions",
+ allow: (context.options[0] && context.options[0].allow) || []
+ };
+
+ /**
+ * Check if variable name is allowed.
+ *
+ * @param {ASTNode} variable The variable to check.
+ * @returns {boolean} Whether or not the variable name is allowed.
+ */
+ function isAllowed(variable) {
+ return options.allow.indexOf(variable.name) !== -1;
+ }
+
+ /**
+ * Checks if a variable of the class name in the class scope of ClassDeclaration.
+ *
+ * ClassDeclaration creates two variables of its name into its outer scope and its class scope.
+ * So we should ignore the variable in the class scope.
+ *
+ * @param {Object} variable The variable to check.
+ * @returns {boolean} Whether or not the variable of the class name in the class scope of ClassDeclaration.
+ */
+ function isDuplicatedClassNameVariable(variable) {
+ const block = variable.scope.block;
+
+ return block.type === "ClassDeclaration" && block.id === variable.identifiers[0];
+ }
+
+ /**
+ * Checks if a variable is inside the initializer of scopeVar.
+ *
+ * To avoid reporting at declarations such as `var a = function a() {};`.
+ * But it should report `var a = function(a) {};` or `var a = function() { function a() {} };`.
+ *
+ * @param {Object} variable The variable to check.
+ * @param {Object} scopeVar The scope variable to look for.
+ * @returns {boolean} Whether or not the variable is inside initializer of scopeVar.
+ */
+ function isOnInitializer(variable, scopeVar) {
+ const outerScope = scopeVar.scope;
+ const outerDef = scopeVar.defs[0];
+ const outer = outerDef && outerDef.parent && outerDef.parent.range;
+ const innerScope = variable.scope;
+ const innerDef = variable.defs[0];
+ const inner = innerDef && innerDef.name.range;
+
+ return (
+ outer &&
+ inner &&
+ outer[0] < inner[0] &&
+ inner[1] < outer[1] &&
+ ((innerDef.type === "FunctionName" && innerDef.node.type === "FunctionExpression") || innerDef.node.type === "ClassExpression") &&
+ outerScope === innerScope.upper
+ );
+ }
+
+ /**
+ * Get a range of a variable's identifier node.
+ * @param {Object} variable The variable to get.
+ * @returns {Array|undefined} The range of the variable's identifier node.
+ */
+ function getNameRange(variable) {
+ const def = variable.defs[0];
+
+ return def && def.name.range;
+ }
+
+ /**
+ * Checks if a variable is in TDZ of scopeVar.
+ * @param {Object} variable The variable to check.
+ * @param {Object} scopeVar The variable of TDZ.
+ * @returns {boolean} Whether or not the variable is in TDZ of scopeVar.
+ */
+ function isInTdz(variable, scopeVar) {
+ const outerDef = scopeVar.defs[0];
+ const inner = getNameRange(variable);
+ const outer = getNameRange(scopeVar);
+
+ return (
+ inner &&
+ outer &&
+ inner[1] < outer[0] &&
+
+ // Excepts FunctionDeclaration if is {"hoist":"function"}.
+ (options.hoist !== "functions" || !outerDef || outerDef.node.type !== "FunctionDeclaration")
+ );
+ }
+
+ /**
+ * Checks the current context for shadowed variables.
+ * @param {Scope} scope - Fixme
+ * @returns {void}
+ */
+ function checkForShadows(scope) {
+ const variables = scope.variables;
+
+ for (let i = 0; i < variables.length; ++i) {
+ const variable = variables[i];
+
+ // Skips "arguments" or variables of a class name in the class scope of ClassDeclaration.
+ if (variable.identifiers.length === 0 ||
+ isDuplicatedClassNameVariable(variable) ||
+ isAllowed(variable)
+ ) {
+ continue;
+ }
+
+ // Gets shadowed variable.
+ const shadowed = astUtils.getVariableByName(scope.upper, variable.name);
+
+ if (shadowed &&
+ (shadowed.identifiers.length > 0 || (options.builtinGlobals && "writeable" in shadowed)) &&
+ !isOnInitializer(variable, shadowed) &&
+ !(options.hoist !== "all" && isInTdz(variable, shadowed))
+ ) {
+ context.report({
+ node: variable.identifiers[0],
+ message: "'{{name}}' is already declared in the upper scope.",
+ data: variable
+ });
+ }
+ }
+ }
+
+ return {
+ "Program:exit"() {
+ const globalScope = context.getScope();
+ const stack = globalScope.childScopes.slice();
+
+ while (stack.length) {
+ const scope = stack.pop();
+
+ stack.push.apply(stack, scope.childScopes);
+ checkForShadows(scope);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-spaced-func.js b/tools/node_modules/eslint/lib/rules/no-spaced-func.js
new file mode 100644
index 0000000000..361c1e0cd7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-spaced-func.js
@@ -0,0 +1,75 @@
+/**
+ * @fileoverview Rule to check that spaced function application
+ * @author Matt DuVall <http://www.mattduvall.com>
+ * @deprecated in ESLint v3.3.0
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow spacing between function identifiers and their applications (deprecated)",
+ category: "Stylistic Issues",
+ recommended: false,
+ replacedBy: ["func-call-spacing"]
+ },
+
+ deprecated: true,
+
+ fixable: "whitespace",
+ schema: []
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Check if open space is present in a function name
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function detectOpenSpaces(node) {
+ const lastCalleeToken = sourceCode.getLastToken(node.callee);
+ let prevToken = lastCalleeToken,
+ parenToken = sourceCode.getTokenAfter(lastCalleeToken);
+
+ // advances to an open parenthesis.
+ while (
+ parenToken &&
+ parenToken.range[1] < node.range[1] &&
+ parenToken.value !== "("
+ ) {
+ prevToken = parenToken;
+ parenToken = sourceCode.getTokenAfter(parenToken);
+ }
+
+ // look for a space between the callee and the open paren
+ if (parenToken &&
+ parenToken.range[1] < node.range[1] &&
+ sourceCode.isSpaceBetweenTokens(prevToken, parenToken)
+ ) {
+ context.report({
+ node,
+ loc: lastCalleeToken.loc.start,
+ message: "Unexpected space between function name and paren.",
+ fix(fixer) {
+ return fixer.removeRange([prevToken.range[1], parenToken.range[0]]);
+ }
+ });
+ }
+ }
+
+ return {
+ CallExpression: detectOpenSpaces,
+ NewExpression: detectOpenSpaces
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-sparse-arrays.js b/tools/node_modules/eslint/lib/rules/no-sparse-arrays.js
new file mode 100644
index 0000000000..3044896c61
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-sparse-arrays.js
@@ -0,0 +1,43 @@
+/**
+ * @fileoverview Disallow sparse arrays
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow sparse arrays",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ ArrayExpression(node) {
+
+ const emptySpot = node.elements.indexOf(null) > -1;
+
+ if (emptySpot) {
+ context.report({ node, message: "Unexpected comma in middle of array." });
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-sync.js b/tools/node_modules/eslint/lib/rules/no-sync.js
new file mode 100644
index 0000000000..06305969a1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-sync.js
@@ -0,0 +1,53 @@
+/**
+ * @fileoverview Rule to check for properties whose identifier ends with the string Sync
+ * @author Matt DuVall<http://mattduvall.com/>
+ */
+
+/* jshint node:true */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow synchronous methods",
+ category: "Node.js and CommonJS",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowAtRootLevel: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const selector = context.options[0] && context.options[0].allowAtRootLevel
+ ? ":function MemberExpression[property.name=/.*Sync$/]"
+ : "MemberExpression[property.name=/.*Sync$/]";
+
+ return {
+ [selector](node) {
+ context.report({
+ node,
+ message: "Unexpected sync method: '{{propertyName}}'.",
+ data: {
+ propertyName: node.property.name
+ }
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-tabs.js b/tools/node_modules/eslint/lib/rules/no-tabs.js
new file mode 100644
index 0000000000..4bab96f387
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-tabs.js
@@ -0,0 +1,47 @@
+/**
+ * @fileoverview Rule to check for tabs inside a file
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+const regex = /\t/;
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow all tabs",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: []
+ },
+
+ create(context) {
+ return {
+ Program(node) {
+ context.getSourceCode().getLines().forEach((line, index) => {
+ const match = regex.exec(line);
+
+ if (match) {
+ context.report({
+ node,
+ loc: {
+ line: index + 1,
+ column: match.index + 1
+ },
+ message: "Unexpected tab character."
+ });
+ }
+ });
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-template-curly-in-string.js b/tools/node_modules/eslint/lib/rules/no-template-curly-in-string.js
new file mode 100644
index 0000000000..d8f6c31108
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-template-curly-in-string.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Warn when using template string syntax in regular strings
+ * @author Jeroen Engels
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow template literal placeholder syntax in regular strings",
+ category: "Possible Errors",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const regex = /\$\{[^}]+\}/;
+
+ return {
+ Literal(node) {
+ if (typeof node.value === "string" && regex.test(node.value)) {
+ context.report({
+ node,
+ message: "Unexpected template string expression."
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-ternary.js b/tools/node_modules/eslint/lib/rules/no-ternary.js
new file mode 100644
index 0000000000..3e254f6812
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-ternary.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview Rule to flag use of ternary operators.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow ternary operators",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ ConditionalExpression(node) {
+ context.report({ node, message: "Ternary operator used." });
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-this-before-super.js b/tools/node_modules/eslint/lib/rules/no-this-before-super.js
new file mode 100644
index 0000000000..2a686ac72e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-this-before-super.js
@@ -0,0 +1,299 @@
+/**
+ * @fileoverview A rule to disallow using `this`/`super` before `super()`.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is a constructor.
+ * @param {ASTNode} node - A node to check. This node type is one of
+ * `Program`, `FunctionDeclaration`, `FunctionExpression`, and
+ * `ArrowFunctionExpression`.
+ * @returns {boolean} `true` if the node is a constructor.
+ */
+function isConstructorFunction(node) {
+ return (
+ node.type === "FunctionExpression" &&
+ node.parent.type === "MethodDefinition" &&
+ node.parent.kind === "constructor"
+ );
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `this`/`super` before calling `super()` in constructors",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /*
+ * Information for each constructor.
+ * - upper: Information of the upper constructor.
+ * - hasExtends: A flag which shows whether the owner class has a valid
+ * `extends` part.
+ * - scope: The scope of the owner class.
+ * - codePath: The code path of this constructor.
+ */
+ let funcInfo = null;
+
+ /*
+ * Information for each code path segment.
+ * Each key is the id of a code path segment.
+ * Each value is an object:
+ * - superCalled: The flag which shows `super()` called in all code paths.
+ * - invalidNodes: The array of invalid ThisExpression and Super nodes.
+ */
+ let segInfoMap = Object.create(null);
+
+ /**
+ * Gets whether or not `super()` is called in a given code path segment.
+ * @param {CodePathSegment} segment - A code path segment to get.
+ * @returns {boolean} `true` if `super()` is called.
+ */
+ function isCalled(segment) {
+ return !segment.reachable || segInfoMap[segment.id].superCalled;
+ }
+
+ /**
+ * Checks whether or not this is in a constructor.
+ * @returns {boolean} `true` if this is in a constructor.
+ */
+ function isInConstructorOfDerivedClass() {
+ return Boolean(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends);
+ }
+
+ /**
+ * Checks whether or not this is before `super()` is called.
+ * @returns {boolean} `true` if this is before `super()` is called.
+ */
+ function isBeforeCallOfSuper() {
+ return (
+ isInConstructorOfDerivedClass() &&
+ !funcInfo.codePath.currentSegments.every(isCalled)
+ );
+ }
+
+ /**
+ * Sets a given node as invalid.
+ * @param {ASTNode} node - A node to set as invalid. This is one of
+ * a ThisExpression and a Super.
+ * @returns {void}
+ */
+ function setInvalid(node) {
+ const segments = funcInfo.codePath.currentSegments;
+
+ for (let i = 0; i < segments.length; ++i) {
+ const segment = segments[i];
+
+ if (segment.reachable) {
+ segInfoMap[segment.id].invalidNodes.push(node);
+ }
+ }
+ }
+
+ /**
+ * Sets the current segment as `super` was called.
+ * @returns {void}
+ */
+ function setSuperCalled() {
+ const segments = funcInfo.codePath.currentSegments;
+
+ for (let i = 0; i < segments.length; ++i) {
+ const segment = segments[i];
+
+ if (segment.reachable) {
+ segInfoMap[segment.id].superCalled = true;
+ }
+ }
+ }
+
+ return {
+
+ /**
+ * Adds information of a constructor into the stack.
+ * @param {CodePath} codePath - A code path which was started.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathStart(codePath, node) {
+ if (isConstructorFunction(node)) {
+
+ // Class > ClassBody > MethodDefinition > FunctionExpression
+ const classNode = node.parent.parent.parent;
+
+ funcInfo = {
+ upper: funcInfo,
+ isConstructor: true,
+ hasExtends: Boolean(
+ classNode.superClass &&
+ !astUtils.isNullOrUndefined(classNode.superClass)
+ ),
+ codePath
+ };
+ } else {
+ funcInfo = {
+ upper: funcInfo,
+ isConstructor: false,
+ hasExtends: false,
+ codePath
+ };
+ }
+ },
+
+ /**
+ * Removes the top of stack item.
+ *
+ * And this treverses all segments of this code path then reports every
+ * invalid node.
+ *
+ * @param {CodePath} codePath - A code path which was ended.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathEnd(codePath) {
+ const isDerivedClass = funcInfo.hasExtends;
+
+ funcInfo = funcInfo.upper;
+ if (!isDerivedClass) {
+ return;
+ }
+
+ codePath.traverseSegments((segment, controller) => {
+ const info = segInfoMap[segment.id];
+
+ for (let i = 0; i < info.invalidNodes.length; ++i) {
+ const invalidNode = info.invalidNodes[i];
+
+ context.report({
+ message: "'{{kind}}' is not allowed before 'super()'.",
+ node: invalidNode,
+ data: {
+ kind: invalidNode.type === "Super" ? "super" : "this"
+ }
+ });
+ }
+
+ if (info.superCalled) {
+ controller.skip();
+ }
+ });
+ },
+
+ /**
+ * Initialize information of a given code path segment.
+ * @param {CodePathSegment} segment - A code path segment to initialize.
+ * @returns {void}
+ */
+ onCodePathSegmentStart(segment) {
+ if (!isInConstructorOfDerivedClass()) {
+ return;
+ }
+
+ // Initialize info.
+ segInfoMap[segment.id] = {
+ superCalled: (
+ segment.prevSegments.length > 0 &&
+ segment.prevSegments.every(isCalled)
+ ),
+ invalidNodes: []
+ };
+ },
+
+ /**
+ * Update information of the code path segment when a code path was
+ * looped.
+ * @param {CodePathSegment} fromSegment - The code path segment of the
+ * end of a loop.
+ * @param {CodePathSegment} toSegment - A code path segment of the head
+ * of a loop.
+ * @returns {void}
+ */
+ onCodePathSegmentLoop(fromSegment, toSegment) {
+ if (!isInConstructorOfDerivedClass()) {
+ return;
+ }
+
+ // Update information inside of the loop.
+ funcInfo.codePath.traverseSegments(
+ { first: toSegment, last: fromSegment },
+ (segment, controller) => {
+ const info = segInfoMap[segment.id];
+
+ if (info.superCalled) {
+ info.invalidNodes = [];
+ controller.skip();
+ } else if (
+ segment.prevSegments.length > 0 &&
+ segment.prevSegments.every(isCalled)
+ ) {
+ info.superCalled = true;
+ info.invalidNodes = [];
+ }
+ }
+ );
+ },
+
+ /**
+ * Reports if this is before `super()`.
+ * @param {ASTNode} node - A target node.
+ * @returns {void}
+ */
+ ThisExpression(node) {
+ if (isBeforeCallOfSuper()) {
+ setInvalid(node);
+ }
+ },
+
+ /**
+ * Reports if this is before `super()`.
+ * @param {ASTNode} node - A target node.
+ * @returns {void}
+ */
+ Super(node) {
+ if (!astUtils.isCallee(node) && isBeforeCallOfSuper()) {
+ setInvalid(node);
+ }
+ },
+
+ /**
+ * Marks `super()` called.
+ * @param {ASTNode} node - A target node.
+ * @returns {void}
+ */
+ "CallExpression:exit"(node) {
+ if (node.callee.type === "Super" && isBeforeCallOfSuper()) {
+ setSuperCalled();
+ }
+ },
+
+ /**
+ * Resets state.
+ * @returns {void}
+ */
+ "Program:exit"() {
+ segInfoMap = Object.create(null);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-throw-literal.js b/tools/node_modules/eslint/lib/rules/no-throw-literal.js
new file mode 100644
index 0000000000..5e9054399a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-throw-literal.js
@@ -0,0 +1,43 @@
+/**
+ * @fileoverview Rule to restrict what can be thrown as an exception.
+ * @author Dieter Oberkofler
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow throwing literals as exceptions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+
+ ThrowStatement(node) {
+ if (!astUtils.couldBeError(node.argument)) {
+ context.report({ node, message: "Expected an object to be thrown." });
+ } else if (node.argument.type === "Identifier") {
+ if (node.argument.name === "undefined") {
+ context.report({ node, message: "Do not throw undefined." });
+ }
+ }
+
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-trailing-spaces.js b/tools/node_modules/eslint/lib/rules/no-trailing-spaces.js
new file mode 100644
index 0000000000..fbbc640217
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-trailing-spaces.js
@@ -0,0 +1,169 @@
+/**
+ * @fileoverview Disallow trailing spaces at the end of lines.
+ * @author Nodeca Team <https://github.com/nodeca>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow trailing whitespace at the end of lines",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ skipBlankLines: {
+ type: "boolean"
+ },
+ ignoreComments: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
+ SKIP_BLANK = `^${BLANK_CLASS}*$`,
+ NONBLANK = `${BLANK_CLASS}+$`;
+
+ const options = context.options[0] || {},
+ skipBlankLines = options.skipBlankLines || false,
+ ignoreComments = typeof options.ignoreComments === "boolean" && options.ignoreComments;
+
+ /**
+ * Report the error message
+ * @param {ASTNode} node node to report
+ * @param {int[]} location range information
+ * @param {int[]} fixRange Range based on the whole program
+ * @returns {void}
+ */
+ function report(node, location, fixRange) {
+
+ /*
+ * Passing node is a bit dirty, because message data will contain big
+ * text in `source`. But... who cares :) ?
+ * One more kludge will not make worse the bloody wizardry of this
+ * plugin.
+ */
+ context.report({
+ node,
+ loc: location,
+ message: "Trailing spaces not allowed.",
+ fix(fixer) {
+ return fixer.removeRange(fixRange);
+ }
+ });
+ }
+
+ /**
+ * Given a list of comment nodes, return the line numbers for those comments.
+ * @param {Array} comments An array of comment nodes.
+ * @returns {number[]} An array of line numbers containing comments.
+ */
+ function getCommentLineNumbers(comments) {
+ const lines = new Set();
+
+ comments.forEach(comment => {
+ for (let i = comment.loc.start.line; i <= comment.loc.end.line; i++) {
+ lines.add(i);
+ }
+ });
+
+ return lines;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ Program: function checkTrailingSpaces(node) {
+
+ /*
+ * Let's hack. Since Espree does not return whitespace nodes,
+ * fetch the source code and do matching via regexps.
+ */
+
+ const re = new RegExp(NONBLANK),
+ skipMatch = new RegExp(SKIP_BLANK),
+ lines = sourceCode.lines,
+ linebreaks = sourceCode.getText().match(astUtils.createGlobalLinebreakMatcher()),
+ comments = sourceCode.getAllComments(),
+ commentLineNumbers = getCommentLineNumbers(comments);
+
+ let totalLength = 0,
+ fixRange = [];
+
+ for (let i = 0, ii = lines.length; i < ii; i++) {
+ const matches = re.exec(lines[i]);
+
+ /*
+ * Always add linebreak length to line length to accommodate for line break (\n or \r\n)
+ * Because during the fix time they also reserve one spot in the array.
+ * Usually linebreak length is 2 for \r\n (CRLF) and 1 for \n (LF)
+ */
+ const linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
+ const lineLength = lines[i].length + linebreakLength;
+
+ if (matches) {
+ const location = {
+ line: i + 1,
+ column: matches.index
+ };
+
+ const rangeStart = totalLength + location.column;
+ const rangeEnd = totalLength + lineLength - linebreakLength;
+ const containingNode = sourceCode.getNodeByRangeIndex(rangeStart);
+
+ if (containingNode && containingNode.type === "TemplateElement" &&
+ rangeStart > containingNode.parent.range[0] &&
+ rangeEnd < containingNode.parent.range[1]) {
+ totalLength += lineLength;
+ continue;
+ }
+
+ /*
+ * If the line has only whitespace, and skipBlankLines
+ * is true, don't report it
+ */
+ if (skipBlankLines && skipMatch.test(lines[i])) {
+ totalLength += lineLength;
+ continue;
+ }
+
+ fixRange = [rangeStart, rangeEnd];
+
+ if (!ignoreComments || !commentLineNumbers.has(location.line)) {
+ report(node, location, fixRange);
+ }
+ }
+
+ totalLength += lineLength;
+ }
+ }
+
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-undef-init.js b/tools/node_modules/eslint/lib/rules/no-undef-init.js
new file mode 100644
index 0000000000..7e58f55a69
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-undef-init.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Rule to flag when initializing to undefined
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow initializing variables to `undefined`",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode();
+
+ return {
+
+ VariableDeclarator(node) {
+ const name = sourceCode.getText(node.id),
+ init = node.init && node.init.name,
+ scope = context.getScope(),
+ undefinedVar = astUtils.getVariableByName(scope, "undefined"),
+ shadowed = undefinedVar && undefinedVar.defs.length > 0;
+
+ if (init === "undefined" && node.parent.kind !== "const" && !shadowed) {
+ context.report({
+ node,
+ message: "It's not necessary to initialize '{{name}}' to undefined.",
+ data: { name },
+ fix(fixer) {
+ if (node.parent.kind === "var") {
+ return null;
+ }
+
+ if (node.id.type === "ArrayPattern" || node.id.type === "ObjectPattern") {
+
+ // Don't fix destructuring assignment to `undefined`.
+ return null;
+ }
+ return fixer.removeRange([node.id.range[1], node.range[1]]);
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-undef.js b/tools/node_modules/eslint/lib/rules/no-undef.js
new file mode 100644
index 0000000000..74a33dd997
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-undef.js
@@ -0,0 +1,71 @@
+/**
+ * @fileoverview Rule to flag references to undeclared variables.
+ * @author Mark Macdonald
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks if the given node is the argument of a typeof operator.
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {boolean} Whether or not the node is the argument of a typeof operator.
+ */
+function hasTypeOfOperator(node) {
+ const parent = node.parent;
+
+ return parent.type === "UnaryExpression" && parent.operator === "typeof";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of undeclared variables unless mentioned in `/*global */` comments",
+ category: "Variables",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ typeof: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0];
+ const considerTypeOf = options && options.typeof === true || false;
+
+ return {
+ "Program:exit"(/* node */) {
+ const globalScope = context.getScope();
+
+ globalScope.through.forEach(ref => {
+ const identifier = ref.identifier;
+
+ if (!considerTypeOf && hasTypeOfOperator(identifier)) {
+ return;
+ }
+
+ context.report({
+ node: identifier,
+ message: "'{{name}}' is not defined.",
+ data: identifier
+ });
+ });
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-undefined.js b/tools/node_modules/eslint/lib/rules/no-undefined.js
new file mode 100644
index 0000000000..7e9f96b921
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-undefined.js
@@ -0,0 +1,77 @@
+/**
+ * @fileoverview Rule to flag references to the undefined variable.
+ * @author Michael Ficarra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `undefined` as an identifier",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Report an invalid "undefined" identifier node.
+ * @param {ASTNode} node The node to report.
+ * @returns {void}
+ */
+ function report(node) {
+ context.report({
+ node,
+ message: "Unexpected use of undefined."
+ });
+ }
+
+ /**
+ * Checks the given scope for references to `undefined` and reports
+ * all references found.
+ * @param {eslint-scope.Scope} scope The scope to check.
+ * @returns {void}
+ */
+ function checkScope(scope) {
+ const undefinedVar = scope.set.get("undefined");
+
+ if (!undefinedVar) {
+ return;
+ }
+
+ const references = undefinedVar.references;
+
+ const defs = undefinedVar.defs;
+
+ // Report non-initializing references (those are covered in defs below)
+ references
+ .filter(ref => !ref.init)
+ .forEach(ref => report(ref.identifier));
+
+ defs.forEach(def => report(def.name));
+ }
+
+ return {
+ "Program:exit"() {
+ const globalScope = context.getScope();
+
+ const stack = [globalScope];
+
+ while (stack.length) {
+ const scope = stack.pop();
+
+ stack.push.apply(stack, scope.childScopes);
+ checkScope(scope);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js
new file mode 100644
index 0000000000..5964da41cd
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js
@@ -0,0 +1,203 @@
+/**
+ * @fileoverview Rule to flag trailing underscores in variable declarations.
+ * @author Matt DuVall <http://www.mattduvall.com>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow dangling underscores in identifiers",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allow: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ allowAfterThis: {
+ type: "boolean"
+ },
+ allowAfterSuper: {
+ type: "boolean"
+ },
+ enforceInMethodNames: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = context.options[0] || {};
+ const ALLOWED_VARIABLES = options.allow ? options.allow : [];
+ const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false;
+ const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
+ const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
+
+ //-------------------------------------------------------------------------
+ // Helpers
+ //-------------------------------------------------------------------------
+
+ /**
+ * Check if identifier is present inside the allowed option
+ * @param {string} identifier name of the node
+ * @returns {boolean} true if its is present
+ * @private
+ */
+ function isAllowed(identifier) {
+ return ALLOWED_VARIABLES.some(ident => ident === identifier);
+ }
+
+ /**
+ * Check if identifier has a underscore at the end
+ * @param {ASTNode} identifier node to evaluate
+ * @returns {boolean} true if its is present
+ * @private
+ */
+ function hasTrailingUnderscore(identifier) {
+ const len = identifier.length;
+
+ return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_");
+ }
+
+ /**
+ * Check if identifier is a special case member expression
+ * @param {ASTNode} identifier node to evaluate
+ * @returns {boolean} true if its is a special case
+ * @private
+ */
+ function isSpecialCaseIdentifierForMemberExpression(identifier) {
+ return identifier === "__proto__";
+ }
+
+ /**
+ * Check if identifier is a special case variable expression
+ * @param {ASTNode} identifier node to evaluate
+ * @returns {boolean} true if its is a special case
+ * @private
+ */
+ function isSpecialCaseIdentifierInVariableExpression(identifier) {
+
+ // Checks for the underscore library usage here
+ return identifier === "_";
+ }
+
+ /**
+ * Check if function has a underscore at the end
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkForTrailingUnderscoreInFunctionDeclaration(node) {
+ if (node.id) {
+ const identifier = node.id.name;
+
+ if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
+ context.report({
+ node,
+ message: "Unexpected dangling '_' in '{{identifier}}'.",
+ data: {
+ identifier
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Check if variable expression has a underscore at the end
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkForTrailingUnderscoreInVariableExpression(node) {
+ const identifier = node.id.name;
+
+ if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
+ !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
+ context.report({
+ node,
+ message: "Unexpected dangling '_' in '{{identifier}}'.",
+ data: {
+ identifier
+ }
+ });
+ }
+ }
+
+ /**
+ * Check if member expression has a underscore at the end
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkForTrailingUnderscoreInMemberExpression(node) {
+ const identifier = node.property.name,
+ isMemberOfThis = node.object.type === "ThisExpression",
+ isMemberOfSuper = node.object.type === "Super";
+
+ if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
+ !(isMemberOfThis && allowAfterThis) &&
+ !(isMemberOfSuper && allowAfterSuper) &&
+ !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) {
+ context.report({
+ node,
+ message: "Unexpected dangling '_' in '{{identifier}}'.",
+ data: {
+ identifier
+ }
+ });
+ }
+ }
+
+ /**
+ * Check if method declaration or method property has a underscore at the end
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkForTrailingUnderscoreInMethod(node) {
+ const identifier = node.key.name;
+ const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method;
+
+ if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier)) {
+ context.report({
+ node,
+ message: "Unexpected dangling '_' in '{{identifier}}'.",
+ data: {
+ identifier
+ }
+ });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ FunctionDeclaration: checkForTrailingUnderscoreInFunctionDeclaration,
+ VariableDeclarator: checkForTrailingUnderscoreInVariableExpression,
+ MemberExpression: checkForTrailingUnderscoreInMemberExpression,
+ MethodDefinition: checkForTrailingUnderscoreInMethod,
+ Property: checkForTrailingUnderscoreInMethod
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unexpected-multiline.js b/tools/node_modules/eslint/lib/rules/no-unexpected-multiline.js
new file mode 100644
index 0000000000..9398b8a603
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unexpected-multiline.js
@@ -0,0 +1,98 @@
+/**
+ * @fileoverview Rule to spot scenarios where a newline looks like it is ending a statement, but is not.
+ * @author Glen Mailer
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow confusing multiline expressions",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ const FUNCTION_MESSAGE = "Unexpected newline between function and ( of function call.";
+ const PROPERTY_MESSAGE = "Unexpected newline between object and [ of property access.";
+ const TAGGED_TEMPLATE_MESSAGE = "Unexpected newline between template tag and template literal.";
+ const DIVISION_MESSAGE = "Unexpected newline between numerator and division operator.";
+
+ const REGEX_FLAG_MATCHER = /^[gimuy]+$/;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Check to see if there is a newline between the node and the following open bracket
+ * line's expression
+ * @param {ASTNode} node The node to check.
+ * @param {string} msg The error message to use.
+ * @returns {void}
+ * @private
+ */
+ function checkForBreakAfter(node, msg) {
+ const openParen = sourceCode.getTokenAfter(node, astUtils.isNotClosingParenToken);
+ const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
+
+ if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
+ context.report({ node, loc: openParen.loc.start, message: msg, data: { char: openParen.value } });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+
+ MemberExpression(node) {
+ if (!node.computed) {
+ return;
+ }
+ checkForBreakAfter(node.object, PROPERTY_MESSAGE);
+ },
+
+ TaggedTemplateExpression(node) {
+ if (node.tag.loc.end.line === node.quasi.loc.start.line) {
+ return;
+ }
+ context.report({ node, loc: node.loc.start, message: TAGGED_TEMPLATE_MESSAGE });
+ },
+
+ CallExpression(node) {
+ if (node.arguments.length === 0) {
+ return;
+ }
+ checkForBreakAfter(node.callee, FUNCTION_MESSAGE);
+ },
+
+ "BinaryExpression[operator='/'] > BinaryExpression[operator='/'].left"(node) {
+ const secondSlash = sourceCode.getTokenAfter(node, token => token.value === "/");
+ const tokenAfterOperator = sourceCode.getTokenAfter(secondSlash);
+
+ if (
+ tokenAfterOperator.type === "Identifier" &&
+ REGEX_FLAG_MATCHER.test(tokenAfterOperator.value) &&
+ secondSlash.range[1] === tokenAfterOperator.range[0]
+ ) {
+ checkForBreakAfter(node.left, DIVISION_MESSAGE);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unmodified-loop-condition.js b/tools/node_modules/eslint/lib/rules/no-unmodified-loop-condition.js
new file mode 100644
index 0000000000..49dff0d0ce
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unmodified-loop-condition.js
@@ -0,0 +1,366 @@
+/**
+ * @fileoverview Rule to disallow use of unmodified expressions in loop conditions
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Traverser = require("../util/traverser"),
+ astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const pushAll = Function.apply.bind(Array.prototype.push);
+const SENTINEL_PATTERN = /(?:(?:Call|Class|Function|Member|New|Yield)Expression|Statement|Declaration)$/;
+const LOOP_PATTERN = /^(?:DoWhile|For|While)Statement$/; // for-in/of statements don't have `test` property.
+const GROUP_PATTERN = /^(?:BinaryExpression|ConditionalExpression)$/;
+const SKIP_PATTERN = /^(?:ArrowFunction|Class|Function)Expression$/;
+const DYNAMIC_PATTERN = /^(?:Call|Member|New|TaggedTemplate|Yield)Expression$/;
+
+/**
+ * @typedef {Object} LoopConditionInfo
+ * @property {eslint-scope.Reference} reference - The reference.
+ * @property {ASTNode} group - BinaryExpression or ConditionalExpression nodes
+ * that the reference is belonging to.
+ * @property {Function} isInLoop - The predicate which checks a given reference
+ * is in this loop.
+ * @property {boolean} modified - The flag that the reference is modified in
+ * this loop.
+ */
+
+/**
+ * Checks whether or not a given reference is a write reference.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the reference is a write reference.
+ */
+function isWriteReference(reference) {
+ if (reference.init) {
+ const def = reference.resolved && reference.resolved.defs[0];
+
+ if (!def || def.type !== "Variable" || def.parent.kind !== "var") {
+ return false;
+ }
+ }
+ return reference.isWrite();
+}
+
+/**
+ * Checks whether or not a given loop condition info does not have the modified
+ * flag.
+ *
+ * @param {LoopConditionInfo} condition - A loop condition info to check.
+ * @returns {boolean} `true` if the loop condition info is "unmodified".
+ */
+function isUnmodified(condition) {
+ return !condition.modified;
+}
+
+/**
+ * Checks whether or not a given loop condition info does not have the modified
+ * flag and does not have the group this condition belongs to.
+ *
+ * @param {LoopConditionInfo} condition - A loop condition info to check.
+ * @returns {boolean} `true` if the loop condition info is "unmodified".
+ */
+function isUnmodifiedAndNotBelongToGroup(condition) {
+ return !(condition.modified || condition.group);
+}
+
+/**
+ * Checks whether or not a given reference is inside of a given node.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the reference is inside of the node.
+ */
+function isInRange(node, reference) {
+ const or = node.range;
+ const ir = reference.identifier.range;
+
+ return or[0] <= ir[0] && ir[1] <= or[1];
+}
+
+/**
+ * Checks whether or not a given reference is inside of a loop node's condition.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the reference is inside of the loop node's
+ * condition.
+ */
+const isInLoop = {
+ WhileStatement: isInRange,
+ DoWhileStatement: isInRange,
+ ForStatement(node, reference) {
+ return (
+ isInRange(node, reference) &&
+ !(node.init && isInRange(node.init, reference))
+ );
+ }
+};
+
+/**
+ * Checks whether or not a given group node has any dynamic elements.
+ *
+ * @param {ASTNode} root - A node to check.
+ * This node is one of BinaryExpression or ConditionalExpression.
+ * @returns {boolean} `true` if the node is dynamic.
+ */
+function hasDynamicExpressions(root) {
+ let retv = false;
+ const traverser = new Traverser();
+
+ traverser.traverse(root, {
+ enter(node) {
+ if (DYNAMIC_PATTERN.test(node.type)) {
+ retv = true;
+ this.break();
+ } else if (SKIP_PATTERN.test(node.type)) {
+ this.skip();
+ }
+ }
+ });
+
+ return retv;
+}
+
+/**
+ * Creates the loop condition information from a given reference.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to create.
+ * @returns {LoopConditionInfo|null} Created loop condition info, or null.
+ */
+function toLoopCondition(reference) {
+ if (reference.init) {
+ return null;
+ }
+
+ let group = null;
+ let child = reference.identifier;
+ let node = child.parent;
+
+ while (node) {
+ if (SENTINEL_PATTERN.test(node.type)) {
+ if (LOOP_PATTERN.test(node.type) && node.test === child) {
+
+ // This reference is inside of a loop condition.
+ return {
+ reference,
+ group,
+ isInLoop: isInLoop[node.type].bind(null, node),
+ modified: false
+ };
+ }
+
+ // This reference is outside of a loop condition.
+ break;
+ }
+
+ /*
+ * If it's inside of a group, OK if either operand is modified.
+ * So stores the group this reference belongs to.
+ */
+ if (GROUP_PATTERN.test(node.type)) {
+
+ // If this expression is dynamic, no need to check.
+ if (hasDynamicExpressions(node)) {
+ break;
+ } else {
+ group = node;
+ }
+ }
+
+ child = node;
+ node = node.parent;
+ }
+
+ return null;
+}
+
+/**
+ * Gets the function which encloses a given reference.
+ * This supports only FunctionDeclaration.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to get.
+ * @returns {ASTNode|null} The function node or null.
+ */
+function getEncloseFunctionDeclaration(reference) {
+ let node = reference.identifier;
+
+ while (node) {
+ if (node.type === "FunctionDeclaration") {
+ return node.id ? node : null;
+ }
+
+ node = node.parent;
+ }
+
+ return null;
+}
+
+/**
+ * Updates the "modified" flags of given loop conditions with given modifiers.
+ *
+ * @param {LoopConditionInfo[]} conditions - The loop conditions to be updated.
+ * @param {eslint-scope.Reference[]} modifiers - The references to update.
+ * @returns {void}
+ */
+function updateModifiedFlag(conditions, modifiers) {
+
+ for (let i = 0; i < conditions.length; ++i) {
+ const condition = conditions[i];
+
+ for (let j = 0; !condition.modified && j < modifiers.length; ++j) {
+ const modifier = modifiers[j];
+ let funcNode, funcVar;
+
+ /*
+ * Besides checking for the condition being in the loop, we want to
+ * check the function that this modifier is belonging to is called
+ * in the loop.
+ * FIXME: This should probably be extracted to a function.
+ */
+ const inLoop = condition.isInLoop(modifier) || Boolean(
+ (funcNode = getEncloseFunctionDeclaration(modifier)) &&
+ (funcVar = astUtils.getVariableByName(modifier.from.upper, funcNode.id.name)) &&
+ funcVar.references.some(condition.isInLoop)
+ );
+
+ condition.modified = inLoop;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unmodified loop conditions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ let groupMap = null;
+
+ /**
+ * Reports a given condition info.
+ *
+ * @param {LoopConditionInfo} condition - A loop condition info to report.
+ * @returns {void}
+ */
+ function report(condition) {
+ const node = condition.reference.identifier;
+
+ context.report({
+ node,
+ message: "'{{name}}' is not modified in this loop.",
+ data: node
+ });
+ }
+
+ /**
+ * Registers given conditions to the group the condition belongs to.
+ *
+ * @param {LoopConditionInfo[]} conditions - A loop condition info to
+ * register.
+ * @returns {void}
+ */
+ function registerConditionsToGroup(conditions) {
+ for (let i = 0; i < conditions.length; ++i) {
+ const condition = conditions[i];
+
+ if (condition.group) {
+ let group = groupMap.get(condition.group);
+
+ if (!group) {
+ group = [];
+ groupMap.set(condition.group, group);
+ }
+ group.push(condition);
+ }
+ }
+ }
+
+ /**
+ * Reports references which are inside of unmodified groups.
+ *
+ * @param {LoopConditionInfo[]} conditions - A loop condition info to report.
+ * @returns {void}
+ */
+ function checkConditionsInGroup(conditions) {
+ if (conditions.every(isUnmodified)) {
+ conditions.forEach(report);
+ }
+ }
+
+ /**
+ * Finds unmodified references which are inside of a loop condition.
+ * Then reports the references which are outside of groups.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to report.
+ * @returns {void}
+ */
+ function checkReferences(variable) {
+
+ // Gets references that exist in loop conditions.
+ const conditions = variable
+ .references
+ .map(toLoopCondition)
+ .filter(Boolean);
+
+ if (conditions.length === 0) {
+ return;
+ }
+
+ // Registers the conditions to belonging groups.
+ registerConditionsToGroup(conditions);
+
+ // Check the conditions are modified.
+ const modifiers = variable.references.filter(isWriteReference);
+
+ if (modifiers.length > 0) {
+ updateModifiedFlag(conditions, modifiers);
+ }
+
+ /*
+ * Reports the conditions which are not belonging to groups.
+ * Others will be reported after all variables are done.
+ */
+ conditions
+ .filter(isUnmodifiedAndNotBelongToGroup)
+ .forEach(report);
+ }
+
+ return {
+ "Program:exit"() {
+ const queue = [context.getScope()];
+
+ groupMap = new Map();
+
+ let scope;
+
+ while ((scope = queue.pop())) {
+ pushAll(queue, scope.childScopes);
+ scope.variables.forEach(checkReferences);
+ }
+
+ groupMap.forEach(checkConditionsInGroup);
+ groupMap = null;
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unneeded-ternary.js b/tools/node_modules/eslint/lib/rules/no-unneeded-ternary.js
new file mode 100644
index 0000000000..5745537805
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unneeded-ternary.js
@@ -0,0 +1,155 @@
+/**
+ * @fileoverview Rule to flag no-unneeded-ternary
+ * @author Gyandeep Singh
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+// Operators that always result in a boolean value
+const BOOLEAN_OPERATORS = new Set(["==", "===", "!=", "!==", ">", ">=", "<", "<=", "in", "instanceof"]);
+const OPERATOR_INVERSES = {
+ "==": "!=",
+ "!=": "==",
+ "===": "!==",
+ "!==": "==="
+
+ // Operators like < and >= are not true inverses, since both will return false with NaN.
+};
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow ternary operators when simpler alternatives exist",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ defaultAssignment: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const defaultAssignment = options.defaultAssignment !== false;
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Test if the node is a boolean literal
+ * @param {ASTNode} node - The node to report.
+ * @returns {boolean} True if the its a boolean literal
+ * @private
+ */
+ function isBooleanLiteral(node) {
+ return node.type === "Literal" && typeof node.value === "boolean";
+ }
+
+ /**
+ * Creates an expression that represents the boolean inverse of the expression represented by the original node
+ * @param {ASTNode} node A node representing an expression
+ * @returns {string} A string representing an inverted expression
+ */
+ function invertExpression(node) {
+ if (node.type === "BinaryExpression" && Object.prototype.hasOwnProperty.call(OPERATOR_INVERSES, node.operator)) {
+ const operatorToken = sourceCode.getFirstTokenBetween(
+ node.left,
+ node.right,
+ token => token.value === node.operator
+ );
+ const text = sourceCode.getText();
+
+ return text.slice(node.range[0],
+ operatorToken.range[0]) + OPERATOR_INVERSES[node.operator] + text.slice(operatorToken.range[1], node.range[1]);
+ }
+
+ if (astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression" })) {
+ return `!(${astUtils.getParenthesisedText(sourceCode, node)})`;
+ }
+ return `!${astUtils.getParenthesisedText(sourceCode, node)}`;
+ }
+
+ /**
+ * Tests if a given node always evaluates to a boolean value
+ * @param {ASTNode} node - An expression node
+ * @returns {boolean} True if it is determined that the node will always evaluate to a boolean value
+ */
+ function isBooleanExpression(node) {
+ return node.type === "BinaryExpression" && BOOLEAN_OPERATORS.has(node.operator) ||
+ node.type === "UnaryExpression" && node.operator === "!";
+ }
+
+ /**
+ * Test if the node matches the pattern id ? id : expression
+ * @param {ASTNode} node - The ConditionalExpression to check.
+ * @returns {boolean} True if the pattern is matched, and false otherwise
+ * @private
+ */
+ function matchesDefaultAssignment(node) {
+ return node.test.type === "Identifier" &&
+ node.consequent.type === "Identifier" &&
+ node.test.name === node.consequent.name;
+ }
+
+ return {
+
+ ConditionalExpression(node) {
+ if (isBooleanLiteral(node.alternate) && isBooleanLiteral(node.consequent)) {
+ context.report({
+ node,
+ loc: node.consequent.loc.start,
+ message: "Unnecessary use of boolean literals in conditional expression.",
+ fix(fixer) {
+ if (node.consequent.value === node.alternate.value) {
+
+ // Replace `foo ? true : true` with just `true`, but don't replace `foo() ? true : true`
+ return node.test.type === "Identifier" ? fixer.replaceText(node, node.consequent.value.toString()) : null;
+ }
+ if (node.alternate.value) {
+
+ // Replace `foo() ? false : true` with `!(foo())`
+ return fixer.replaceText(node, invertExpression(node.test));
+ }
+
+ // Replace `foo ? true : false` with `foo` if `foo` is guaranteed to be a boolean, or `!!foo` otherwise.
+
+ return fixer.replaceText(node, isBooleanExpression(node.test) ? astUtils.getParenthesisedText(sourceCode, node.test) : `!${invertExpression(node.test)}`);
+ }
+ });
+ } else if (!defaultAssignment && matchesDefaultAssignment(node)) {
+ context.report({
+ node,
+ loc: node.consequent.loc.start,
+ message: "Unnecessary use of conditional expression for default assignment.",
+ fix: fixer => {
+ let nodeAlternate = astUtils.getParenthesisedText(sourceCode, node.alternate);
+
+ if (node.alternate.type === "ConditionalExpression") {
+ const isAlternateParenthesised = astUtils.isParenthesised(sourceCode, node.alternate);
+
+ nodeAlternate = isAlternateParenthesised ? nodeAlternate : `(${nodeAlternate})`;
+ }
+
+ return fixer.replaceText(node, `${astUtils.getParenthesisedText(sourceCode, node.test)} || ${nodeAlternate}`);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unreachable.js b/tools/node_modules/eslint/lib/rules/no-unreachable.js
new file mode 100644
index 0000000000..217a6a4299
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unreachable.js
@@ -0,0 +1,212 @@
+/**
+ * @fileoverview Checks for unreachable code due to return, throws, break, and continue.
+ * @author Joel Feenstra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given variable declarator has the initializer.
+ * @param {ASTNode} node - A VariableDeclarator node to check.
+ * @returns {boolean} `true` if the node has the initializer.
+ */
+function isInitialized(node) {
+ return Boolean(node.init);
+}
+
+/**
+ * Checks whether or not a given code path segment is unreachable.
+ * @param {CodePathSegment} segment - A CodePathSegment to check.
+ * @returns {boolean} `true` if the segment is unreachable.
+ */
+function isUnreachable(segment) {
+ return !segment.reachable;
+}
+
+/**
+ * The class to distinguish consecutive unreachable statements.
+ */
+class ConsecutiveRange {
+ constructor(sourceCode) {
+ this.sourceCode = sourceCode;
+ this.startNode = null;
+ this.endNode = null;
+ }
+
+ /**
+ * The location object of this range.
+ * @type {Object}
+ */
+ get location() {
+ return {
+ start: this.startNode.loc.start,
+ end: this.endNode.loc.end
+ };
+ }
+
+ /**
+ * `true` if this range is empty.
+ * @type {boolean}
+ */
+ get isEmpty() {
+ return !(this.startNode && this.endNode);
+ }
+
+ /**
+ * Checks whether the given node is inside of this range.
+ * @param {ASTNode|Token} node - The node to check.
+ * @returns {boolean} `true` if the node is inside of this range.
+ */
+ contains(node) {
+ return (
+ node.range[0] >= this.startNode.range[0] &&
+ node.range[1] <= this.endNode.range[1]
+ );
+ }
+
+ /**
+ * Checks whether the given node is consecutive to this range.
+ * @param {ASTNode} node - The node to check.
+ * @returns {boolean} `true` if the node is consecutive to this range.
+ */
+ isConsecutive(node) {
+ return this.contains(this.sourceCode.getTokenBefore(node));
+ }
+
+ /**
+ * Merges the given node to this range.
+ * @param {ASTNode} node - The node to merge.
+ * @returns {void}
+ */
+ merge(node) {
+ this.endNode = node;
+ }
+
+ /**
+ * Resets this range by the given node or null.
+ * @param {ASTNode|null} node - The node to reset, or null.
+ * @returns {void}
+ */
+ reset(node) {
+ this.startNode = this.endNode = node;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ let currentCodePath = null;
+
+ const range = new ConsecutiveRange(context.getSourceCode());
+
+ /**
+ * Reports a given node if it's unreachable.
+ * @param {ASTNode} node - A statement node to report.
+ * @returns {void}
+ */
+ function reportIfUnreachable(node) {
+ let nextNode = null;
+
+ if (node && currentCodePath.currentSegments.every(isUnreachable)) {
+
+ // Store this statement to distinguish consecutive statements.
+ if (range.isEmpty) {
+ range.reset(node);
+ return;
+ }
+
+ // Skip if this statement is inside of the current range.
+ if (range.contains(node)) {
+ return;
+ }
+
+ // Merge if this statement is consecutive to the current range.
+ if (range.isConsecutive(node)) {
+ range.merge(node);
+ return;
+ }
+
+ nextNode = node;
+ }
+
+ /*
+ * Report the current range since this statement is reachable or is
+ * not consecutive to the current range.
+ */
+ if (!range.isEmpty) {
+ context.report({
+ message: "Unreachable code.",
+ loc: range.location,
+ node: range.startNode
+ });
+ }
+
+ // Update the current range.
+ range.reset(nextNode);
+ }
+
+ return {
+
+ // Manages the current code path.
+ onCodePathStart(codePath) {
+ currentCodePath = codePath;
+ },
+
+ onCodePathEnd() {
+ currentCodePath = currentCodePath.upper;
+ },
+
+ // Registers for all statement nodes (excludes FunctionDeclaration).
+ BlockStatement: reportIfUnreachable,
+ BreakStatement: reportIfUnreachable,
+ ClassDeclaration: reportIfUnreachable,
+ ContinueStatement: reportIfUnreachable,
+ DebuggerStatement: reportIfUnreachable,
+ DoWhileStatement: reportIfUnreachable,
+ EmptyStatement: reportIfUnreachable,
+ ExpressionStatement: reportIfUnreachable,
+ ForInStatement: reportIfUnreachable,
+ ForOfStatement: reportIfUnreachable,
+ ForStatement: reportIfUnreachable,
+ IfStatement: reportIfUnreachable,
+ ImportDeclaration: reportIfUnreachable,
+ LabeledStatement: reportIfUnreachable,
+ ReturnStatement: reportIfUnreachable,
+ SwitchStatement: reportIfUnreachable,
+ ThrowStatement: reportIfUnreachable,
+ TryStatement: reportIfUnreachable,
+
+ VariableDeclaration(node) {
+ if (node.kind !== "var" || node.declarations.some(isInitialized)) {
+ reportIfUnreachable(node);
+ }
+ },
+
+ WhileStatement: reportIfUnreachable,
+ WithStatement: reportIfUnreachable,
+ ExportNamedDeclaration: reportIfUnreachable,
+ ExportDefaultDeclaration: reportIfUnreachable,
+ ExportAllDeclaration: reportIfUnreachable,
+
+ "Program:exit"() {
+ reportIfUnreachable();
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unsafe-finally.js b/tools/node_modules/eslint/lib/rules/no-unsafe-finally.js
new file mode 100644
index 0000000000..d25033e545
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unsafe-finally.js
@@ -0,0 +1,104 @@
+/**
+ * @fileoverview Rule to flag unsafe statements in finally block
+ * @author Onur Temizkan
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const SENTINEL_NODE_TYPE_RETURN_THROW = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression)$/;
+const SENTINEL_NODE_TYPE_BREAK = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement|SwitchStatement)$/;
+const SENTINEL_NODE_TYPE_CONTINUE = /^(?:Program|(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|DoWhileStatement|WhileStatement|ForOfStatement|ForInStatement|ForStatement)$/;
+
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow control flow statements in `finally` blocks",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+ create(context) {
+
+ /**
+ * Checks if the node is the finalizer of a TryStatement
+ *
+ * @param {ASTNode} node - node to check.
+ * @returns {boolean} - true if the node is the finalizer of a TryStatement
+ */
+ function isFinallyBlock(node) {
+ return node.parent.type === "TryStatement" && node.parent.finalizer === node;
+ }
+
+ /**
+ * Climbs up the tree if the node is not a sentinel node
+ *
+ * @param {ASTNode} node - node to check.
+ * @param {string} label - label of the break or continue statement
+ * @returns {boolean} - return whether the node is a finally block or a sentinel node
+ */
+ function isInFinallyBlock(node, label) {
+ let labelInside = false;
+ let sentinelNodeType;
+
+ if (node.type === "BreakStatement" && !node.label) {
+ sentinelNodeType = SENTINEL_NODE_TYPE_BREAK;
+ } else if (node.type === "ContinueStatement") {
+ sentinelNodeType = SENTINEL_NODE_TYPE_CONTINUE;
+ } else {
+ sentinelNodeType = SENTINEL_NODE_TYPE_RETURN_THROW;
+ }
+
+ while (node && !sentinelNodeType.test(node.type)) {
+ if (node.parent.label && label && (node.parent.label.name === label.name)) {
+ labelInside = true;
+ }
+ if (isFinallyBlock(node)) {
+ if (label && labelInside) {
+ return false;
+ }
+ return true;
+ }
+ node = node.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether the possibly-unsafe statement is inside a finally block.
+ *
+ * @param {ASTNode} node - node to check.
+ * @returns {void}
+ */
+ function check(node) {
+ if (isInFinallyBlock(node, node.label)) {
+ context.report({
+ message: "Unsafe usage of {{nodeType}}.",
+ data: {
+ nodeType: node.type
+ },
+ node,
+ line: node.loc.line,
+ column: node.loc.column
+ });
+ }
+ }
+
+ return {
+ ReturnStatement: check,
+ ThrowStatement: check,
+ BreakStatement: check,
+ ContinueStatement: check
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unsafe-negation.js b/tools/node_modules/eslint/lib/rules/no-unsafe-negation.js
new file mode 100644
index 0000000000..761dc03386
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unsafe-negation.js
@@ -0,0 +1,80 @@
+/**
+ * @fileoverview Rule to disallow negating the left operand of relational operators
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether the given operator is a relational operator or not.
+ *
+ * @param {string} op - The operator type to check.
+ * @returns {boolean} `true` if the operator is a relational operator.
+ */
+function isRelationalOperator(op) {
+ return op === "in" || op === "instanceof";
+}
+
+/**
+ * Checks whether the given node is a logical negation expression or not.
+ *
+ * @param {ASTNode} node - The node to check.
+ * @returns {boolean} `true` if the node is a logical negation expression.
+ */
+function isNegation(node) {
+ return node.type === "UnaryExpression" && node.operator === "!";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow negating the left operand of relational operators",
+ category: "Possible Errors",
+ recommended: true
+ },
+ schema: [],
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ BinaryExpression(node) {
+ if (isRelationalOperator(node.operator) &&
+ isNegation(node.left) &&
+ !astUtils.isParenthesised(sourceCode, node.left)
+ ) {
+ context.report({
+ node,
+ loc: node.left.loc,
+ message: "Unexpected negating the left operand of '{{operator}}' operator.",
+ data: node,
+
+ fix(fixer) {
+ const negationToken = sourceCode.getFirstToken(node.left);
+ const fixRange = [negationToken.range[1], node.range[1]];
+ const text = sourceCode.text.slice(fixRange[0], fixRange[1]);
+
+ return fixer.replaceTextRange(fixRange, `(${text})`);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unused-expressions.js b/tools/node_modules/eslint/lib/rules/no-unused-expressions.js
new file mode 100644
index 0000000000..b4e1074d54
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unused-expressions.js
@@ -0,0 +1,126 @@
+/**
+ * @fileoverview Flag expressions in statement position that do not side effect
+ * @author Michael Ficarra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unused expressions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowShortCircuit: {
+ type: "boolean"
+ },
+ allowTernary: {
+ type: "boolean"
+ },
+ allowTaggedTemplates: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const config = context.options[0] || {},
+ allowShortCircuit = config.allowShortCircuit || false,
+ allowTernary = config.allowTernary || false,
+ allowTaggedTemplates = config.allowTaggedTemplates || false;
+
+ /**
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node structurally represents a directive
+ */
+ function looksLikeDirective(node) {
+ return node.type === "ExpressionStatement" &&
+ node.expression.type === "Literal" && typeof node.expression.value === "string";
+ }
+
+ /**
+ * @param {Function} predicate - ([a] -> Boolean) the function used to make the determination
+ * @param {a[]} list - the input list
+ * @returns {a[]} the leading sequence of members in the given list that pass the given predicate
+ */
+ function takeWhile(predicate, list) {
+ for (let i = 0; i < list.length; ++i) {
+ if (!predicate(list[i])) {
+ return list.slice(0, i);
+ }
+ }
+ return list.slice();
+ }
+
+ /**
+ * @param {ASTNode} node - a Program or BlockStatement node
+ * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
+ */
+ function directives(node) {
+ return takeWhile(looksLikeDirective, node.body);
+ }
+
+ /**
+ * @param {ASTNode} node - any node
+ * @param {ASTNode[]} ancestors - the given node's ancestors
+ * @returns {boolean} whether the given node is considered a directive in its current position
+ */
+ function isDirective(node, ancestors) {
+ const parent = ancestors[ancestors.length - 1],
+ grandparent = ancestors[ancestors.length - 2];
+
+ return (parent.type === "Program" || parent.type === "BlockStatement" &&
+ (/Function/.test(grandparent.type))) &&
+ directives(parent).indexOf(node) >= 0;
+ }
+
+ /**
+ * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags.
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node is a valid expression
+ */
+ function isValidExpression(node) {
+ if (allowTernary) {
+
+ // Recursive check for ternary and logical expressions
+ if (node.type === "ConditionalExpression") {
+ return isValidExpression(node.consequent) && isValidExpression(node.alternate);
+ }
+ }
+
+ if (allowShortCircuit) {
+ if (node.type === "LogicalExpression") {
+ return isValidExpression(node.right);
+ }
+ }
+
+ if (allowTaggedTemplates && node.type === "TaggedTemplateExpression") {
+ return true;
+ }
+
+ return /^(?:Assignment|Call|New|Update|Yield|Await)Expression$/.test(node.type) ||
+ (node.type === "UnaryExpression" && ["delete", "void"].indexOf(node.operator) >= 0);
+ }
+
+ return {
+ ExpressionStatement(node) {
+ if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) {
+ context.report({ node, message: "Expected an assignment or function call and instead saw an expression." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unused-labels.js b/tools/node_modules/eslint/lib/rules/no-unused-labels.js
new file mode 100644
index 0000000000..bcd3cfdc47
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unused-labels.js
@@ -0,0 +1,106 @@
+/**
+ * @fileoverview Rule to disallow unused labels.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unused labels",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ let scopeInfo = null;
+
+ /**
+ * Adds a scope info to the stack.
+ *
+ * @param {ASTNode} node - A node to add. This is a LabeledStatement.
+ * @returns {void}
+ */
+ function enterLabeledScope(node) {
+ scopeInfo = {
+ label: node.label.name,
+ used: false,
+ upper: scopeInfo
+ };
+ }
+
+ /**
+ * Removes the top of the stack.
+ * At the same time, this reports the label if it's never used.
+ *
+ * @param {ASTNode} node - A node to report. This is a LabeledStatement.
+ * @returns {void}
+ */
+ function exitLabeledScope(node) {
+ if (!scopeInfo.used) {
+ context.report({
+ node: node.label,
+ message: "'{{name}}:' is defined but never used.",
+ data: node.label,
+ fix(fixer) {
+
+ /*
+ * Only perform a fix if there are no comments between the label and the body. This will be the case
+ * when there is exactly one token/comment (the ":") between the label and the body.
+ */
+ if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
+ sourceCode.getTokenBefore(node.body, { includeComments: true })) {
+ return fixer.removeRange([node.range[0], node.body.range[0]]);
+ }
+
+ return null;
+ }
+ });
+ }
+
+ scopeInfo = scopeInfo.upper;
+ }
+
+ /**
+ * Marks the label of a given node as used.
+ *
+ * @param {ASTNode} node - A node to mark. This is a BreakStatement or
+ * ContinueStatement.
+ * @returns {void}
+ */
+ function markAsUsed(node) {
+ if (!node.label) {
+ return;
+ }
+
+ const label = node.label.name;
+ let info = scopeInfo;
+
+ while (info) {
+ if (info.label === label) {
+ info.used = true;
+ break;
+ }
+ info = info.upper;
+ }
+ }
+
+ return {
+ LabeledStatement: enterLabeledScope,
+ "LabeledStatement:exit": exitLabeledScope,
+ BreakStatement: markAsUsed,
+ ContinueStatement: markAsUsed
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-unused-vars.js b/tools/node_modules/eslint/lib/rules/no-unused-vars.js
new file mode 100644
index 0000000000..05940d5932
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-unused-vars.js
@@ -0,0 +1,647 @@
+/**
+ * @fileoverview Rule to flag declared but unused variables
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unused variables",
+ category: "Variables",
+ recommended: true
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["all", "local"]
+ },
+ {
+ type: "object",
+ properties: {
+ vars: {
+ enum: ["all", "local"]
+ },
+ varsIgnorePattern: {
+ type: "string"
+ },
+ args: {
+ enum: ["all", "after-used", "none"]
+ },
+ ignoreRestSiblings: {
+ type: "boolean"
+ },
+ argsIgnorePattern: {
+ type: "string"
+ },
+ caughtErrors: {
+ enum: ["all", "none"]
+ },
+ caughtErrorsIgnorePattern: {
+ type: "string"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ const REST_PROPERTY_TYPE = /^(?:Experimental)?RestProperty$/;
+
+ const config = {
+ vars: "all",
+ args: "after-used",
+ ignoreRestSiblings: false,
+ caughtErrors: "none"
+ };
+
+ const firstOption = context.options[0];
+
+ if (firstOption) {
+ if (typeof firstOption === "string") {
+ config.vars = firstOption;
+ } else {
+ config.vars = firstOption.vars || config.vars;
+ config.args = firstOption.args || config.args;
+ config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
+ config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
+
+ if (firstOption.varsIgnorePattern) {
+ config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern);
+ }
+
+ if (firstOption.argsIgnorePattern) {
+ config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern);
+ }
+
+ if (firstOption.caughtErrorsIgnorePattern) {
+ config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern);
+ }
+ }
+ }
+
+ /**
+ * Generate the warning message about the variable being
+ * defined and unused, including the ignore pattern if configured.
+ * @param {Variable} unusedVar - eslint-scope variable object.
+ * @returns {string} The warning message to be used with this unused variable.
+ */
+ function getDefinedMessage(unusedVar) {
+ let type;
+ let pattern;
+
+ if (config.varsIgnorePattern) {
+ type = "vars";
+ pattern = config.varsIgnorePattern.toString();
+ }
+
+ if (unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type) {
+ const defType = unusedVar.defs[0].type;
+
+ if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) {
+ type = "args";
+ pattern = config.caughtErrorsIgnorePattern.toString();
+ } else if (defType === "Parameter" && config.argsIgnorePattern) {
+ type = "args";
+ pattern = config.argsIgnorePattern.toString();
+ }
+ }
+
+ const additional = type ? ` Allowed unused ${type} must match ${pattern}.` : "";
+
+ return `'{{name}}' is defined but never used.${additional}`;
+ }
+
+ /**
+ * Generate the warning message about the variable being
+ * assigned and unused, including the ignore pattern if configured.
+ * @returns {string} The warning message to be used with this unused variable.
+ */
+ function getAssignedMessage() {
+ const additional = config.varsIgnorePattern ? ` Allowed unused vars must match ${config.varsIgnorePattern.toString()}.` : "";
+
+ return `'{{name}}' is assigned a value but never used.${additional}`;
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const STATEMENT_TYPE = /(?:Statement|Declaration)$/;
+
+ /**
+ * Determines if a given variable is being exported from a module.
+ * @param {Variable} variable - eslint-scope variable object.
+ * @returns {boolean} True if the variable is exported, false if not.
+ * @private
+ */
+ function isExported(variable) {
+
+ const definition = variable.defs[0];
+
+ if (definition) {
+
+ let node = definition.node;
+
+ if (node.type === "VariableDeclarator") {
+ node = node.parent;
+ } else if (definition.type === "Parameter") {
+ return false;
+ }
+
+ return node.parent.type.indexOf("Export") === 0;
+ }
+ return false;
+
+ }
+
+ /**
+ * Determines if a variable has a sibling rest property
+ * @param {Variable} variable - eslint-scope variable object.
+ * @returns {boolean} True if the variable is exported, false if not.
+ * @private
+ */
+ function hasRestSpreadSibling(variable) {
+ if (config.ignoreRestSiblings) {
+ return variable.defs.some(def => {
+ const propertyNode = def.name.parent;
+ const patternNode = propertyNode.parent;
+
+ return (
+ propertyNode.type === "Property" &&
+ patternNode.type === "ObjectPattern" &&
+ REST_PROPERTY_TYPE.test(patternNode.properties[patternNode.properties.length - 1].type)
+ );
+ });
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if a reference is a read operation.
+ * @param {Reference} ref - An eslint-scope Reference
+ * @returns {boolean} whether the given reference represents a read operation
+ * @private
+ */
+ function isReadRef(ref) {
+ return ref.isRead();
+ }
+
+ /**
+ * Determine if an identifier is referencing an enclosing function name.
+ * @param {Reference} ref - The reference to check.
+ * @param {ASTNode[]} nodes - The candidate function nodes.
+ * @returns {boolean} True if it's a self-reference, false if not.
+ * @private
+ */
+ function isSelfReference(ref, nodes) {
+ let scope = ref.from;
+
+ while (scope) {
+ if (nodes.indexOf(scope.block) >= 0) {
+ return true;
+ }
+
+ scope = scope.upper;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks the position of given nodes.
+ *
+ * @param {ASTNode} inner - A node which is expected as inside.
+ * @param {ASTNode} outer - A node which is expected as outside.
+ * @returns {boolean} `true` if the `inner` node exists in the `outer` node.
+ * @private
+ */
+ function isInside(inner, outer) {
+ return (
+ inner.range[0] >= outer.range[0] &&
+ inner.range[1] <= outer.range[1]
+ );
+ }
+
+ /**
+ * If a given reference is left-hand side of an assignment, this gets
+ * the right-hand side node of the assignment.
+ *
+ * In the following cases, this returns null.
+ *
+ * - The reference is not the LHS of an assignment expression.
+ * - The reference is inside of a loop.
+ * - The reference is inside of a function scope which is different from
+ * the declaration.
+ *
+ * @param {eslint-scope.Reference} ref - A reference to check.
+ * @param {ASTNode} prevRhsNode - The previous RHS node.
+ * @returns {ASTNode|null} The RHS node or null.
+ * @private
+ */
+ function getRhsNode(ref, prevRhsNode) {
+ const id = ref.identifier;
+ const parent = id.parent;
+ const granpa = parent.parent;
+ const refScope = ref.from.variableScope;
+ const varScope = ref.resolved.scope.variableScope;
+ const canBeUsedLater = refScope !== varScope || astUtils.isInLoop(id);
+
+ /*
+ * Inherits the previous node if this reference is in the node.
+ * This is for `a = a + a`-like code.
+ */
+ if (prevRhsNode && isInside(id, prevRhsNode)) {
+ return prevRhsNode;
+ }
+
+ if (parent.type === "AssignmentExpression" &&
+ granpa.type === "ExpressionStatement" &&
+ id === parent.left &&
+ !canBeUsedLater
+ ) {
+ return parent.right;
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether a given function node is stored to somewhere or not.
+ * If the function node is stored, the function can be used later.
+ *
+ * @param {ASTNode} funcNode - A function node to check.
+ * @param {ASTNode} rhsNode - The RHS node of the previous assignment.
+ * @returns {boolean} `true` if under the following conditions:
+ * - the funcNode is assigned to a variable.
+ * - the funcNode is bound as an argument of a function call.
+ * - the function is bound to a property and the object satisfies above conditions.
+ * @private
+ */
+ function isStorableFunction(funcNode, rhsNode) {
+ let node = funcNode;
+ let parent = funcNode.parent;
+
+ while (parent && isInside(parent, rhsNode)) {
+ switch (parent.type) {
+ case "SequenceExpression":
+ if (parent.expressions[parent.expressions.length - 1] !== node) {
+ return false;
+ }
+ break;
+
+ case "CallExpression":
+ case "NewExpression":
+ return parent.callee !== node;
+
+ case "AssignmentExpression":
+ case "TaggedTemplateExpression":
+ case "YieldExpression":
+ return true;
+
+ default:
+ if (STATEMENT_TYPE.test(parent.type)) {
+
+ /*
+ * If it encountered statements, this is a complex pattern.
+ * Since analyzeing complex patterns is hard, this returns `true` to avoid false positive.
+ */
+ return true;
+ }
+ }
+
+ node = parent;
+ parent = parent.parent;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether a given Identifier node exists inside of a function node which can be used later.
+ *
+ * "can be used later" means:
+ * - the function is assigned to a variable.
+ * - the function is bound to a property and the object can be used later.
+ * - the function is bound as an argument of a function call.
+ *
+ * If a reference exists in a function which can be used later, the reference is read when the function is called.
+ *
+ * @param {ASTNode} id - An Identifier node to check.
+ * @param {ASTNode} rhsNode - The RHS node of the previous assignment.
+ * @returns {boolean} `true` if the `id` node exists inside of a function node which can be used later.
+ * @private
+ */
+ function isInsideOfStorableFunction(id, rhsNode) {
+ const funcNode = astUtils.getUpperFunction(id);
+
+ return (
+ funcNode &&
+ isInside(funcNode, rhsNode) &&
+ isStorableFunction(funcNode, rhsNode)
+ );
+ }
+
+ /**
+ * Checks whether a given reference is a read to update itself or not.
+ *
+ * @param {eslint-scope.Reference} ref - A reference to check.
+ * @param {ASTNode} rhsNode - The RHS node of the previous assignment.
+ * @returns {boolean} The reference is a read to update itself.
+ * @private
+ */
+ function isReadForItself(ref, rhsNode) {
+ const id = ref.identifier;
+ const parent = id.parent;
+ const granpa = parent.parent;
+
+ return ref.isRead() && (
+
+ // self update. e.g. `a += 1`, `a++`
+ (
+ parent.type === "AssignmentExpression" &&
+ granpa.type === "ExpressionStatement" &&
+ parent.left === id
+ ) ||
+ (
+ parent.type === "UpdateExpression" &&
+ granpa.type === "ExpressionStatement"
+ ) ||
+
+ // in RHS of an assignment for itself. e.g. `a = a + 1`
+ (
+ rhsNode &&
+ isInside(id, rhsNode) &&
+ !isInsideOfStorableFunction(id, rhsNode)
+ )
+ );
+ }
+
+ /**
+ * Determine if an identifier is used either in for-in loops.
+ *
+ * @param {Reference} ref - The reference to check.
+ * @returns {boolean} whether reference is used in the for-in loops
+ * @private
+ */
+ function isForInRef(ref) {
+ let target = ref.identifier.parent;
+
+
+ // "for (var ...) { return; }"
+ if (target.type === "VariableDeclarator") {
+ target = target.parent.parent;
+ }
+
+ if (target.type !== "ForInStatement") {
+ return false;
+ }
+
+ // "for (...) { return; }"
+ if (target.body.type === "BlockStatement") {
+ target = target.body.body[0];
+
+ // "for (...) return;"
+ } else {
+ target = target.body;
+ }
+
+ // For empty loop body
+ if (!target) {
+ return false;
+ }
+
+ return target.type === "ReturnStatement";
+ }
+
+ /**
+ * Determines if the variable is used.
+ * @param {Variable} variable - The variable to check.
+ * @returns {boolean} True if the variable is used
+ * @private
+ */
+ function isUsedVariable(variable) {
+ const functionNodes = variable.defs.filter(def => def.type === "FunctionName").map(def => def.node),
+ isFunctionDefinition = functionNodes.length > 0;
+ let rhsNode = null;
+
+ return variable.references.some(ref => {
+ if (isForInRef(ref)) {
+ return true;
+ }
+
+ const forItself = isReadForItself(ref, rhsNode);
+
+ rhsNode = getRhsNode(ref, rhsNode);
+
+ return (
+ isReadRef(ref) &&
+ !forItself &&
+ !(isFunctionDefinition && isSelfReference(ref, functionNodes))
+ );
+ });
+ }
+
+ /**
+ * Checks whether the given variable is the last parameter in the non-ignored parameters.
+ *
+ * @param {eslint-scope.Variable} variable - The variable to check.
+ * @returns {boolean} `true` if the variable is the last.
+ */
+ function isLastInNonIgnoredParameters(variable) {
+ const def = variable.defs[0];
+
+ // This is the last.
+ if (def.index === def.node.params.length - 1) {
+ return true;
+ }
+
+ // if all parameters preceded by this variable are ignored and unused, this is the last.
+ if (config.argsIgnorePattern) {
+ const params = context.getDeclaredVariables(def.node);
+ const posteriorParams = params.slice(params.indexOf(variable) + 1);
+
+ if (posteriorParams.every(v => v.references.length === 0 && config.argsIgnorePattern.test(v.name))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets an array of variables without read references.
+ * @param {Scope} scope - an eslint-scope Scope object.
+ * @param {Variable[]} unusedVars - an array that saving result.
+ * @returns {Variable[]} unused variables of the scope and descendant scopes.
+ * @private
+ */
+ function collectUnusedVariables(scope, unusedVars) {
+ const variables = scope.variables;
+ const childScopes = scope.childScopes;
+ let i, l;
+
+ if (scope.type !== "TDZ" && (scope.type !== "global" || config.vars === "all")) {
+ for (i = 0, l = variables.length; i < l; ++i) {
+ const variable = variables[i];
+
+ // skip a variable of class itself name in the class scope
+ if (scope.type === "class" && scope.block.id === variable.identifiers[0]) {
+ continue;
+ }
+
+ // skip function expression names and variables marked with markVariableAsUsed()
+ if (scope.functionExpressionScope || variable.eslintUsed) {
+ continue;
+ }
+
+ // skip implicit "arguments" variable
+ if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) {
+ continue;
+ }
+
+ // explicit global variables don't have definitions.
+ const def = variable.defs[0];
+
+ if (def) {
+ const type = def.type;
+
+ // skip catch variables
+ if (type === "CatchClause") {
+ if (config.caughtErrors === "none") {
+ continue;
+ }
+
+ // skip ignored parameters
+ if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
+ continue;
+ }
+ }
+
+ if (type === "Parameter") {
+
+ // skip any setter argument
+ if ((def.node.parent.type === "Property" || def.node.parent.type === "MethodDefinition") && def.node.parent.kind === "set") {
+ continue;
+ }
+
+ // if "args" option is "none", skip any parameter
+ if (config.args === "none") {
+ continue;
+ }
+
+ // skip ignored parameters
+ if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {
+ continue;
+ }
+
+ // if "args" option is "after-used", skip all but the last parameter
+ if (config.args === "after-used" && astUtils.isFunction(def.name.parent) && !isLastInNonIgnoredParameters(variable)) {
+ continue;
+ }
+ } else {
+
+ // skip ignored variables
+ if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {
+ continue;
+ }
+ }
+ }
+
+ if (!isUsedVariable(variable) && !isExported(variable) && !hasRestSpreadSibling(variable)) {
+ unusedVars.push(variable);
+ }
+ }
+ }
+
+ for (i = 0, l = childScopes.length; i < l; ++i) {
+ collectUnusedVariables(childScopes[i], unusedVars);
+ }
+
+ return unusedVars;
+ }
+
+ /**
+ * Gets the index of a given variable name in a given comment.
+ * @param {eslint-scope.Variable} variable - A variable to get.
+ * @param {ASTNode} comment - A comment node which includes the variable name.
+ * @returns {number} The index of the variable name's location.
+ * @private
+ */
+ function getColumnInComment(variable, comment) {
+ const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(variable.name)}(?:$|[\\s,:])`, "g");
+
+ // To ignore the first text "global".
+ namePattern.lastIndex = comment.value.indexOf("global") + 6;
+
+ // Search a given variable name.
+ const match = namePattern.exec(comment.value);
+
+ return match ? match.index + 1 : 0;
+ }
+
+ /**
+ * Creates the correct location of a given variables.
+ * The location is at its name string in a `/*global` comment.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to get its location.
+ * @returns {{line: number, column: number}} The location object for the variable.
+ * @private
+ */
+ function getLocation(variable) {
+ const comment = variable.eslintExplicitGlobalComment;
+
+ return sourceCode.getLocFromIndex(comment.range[0] + 2 + getColumnInComment(variable, comment));
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ "Program:exit"(programNode) {
+ const unusedVars = collectUnusedVariables(context.getScope(), []);
+
+ for (let i = 0, l = unusedVars.length; i < l; ++i) {
+ const unusedVar = unusedVars[i];
+
+ if (unusedVar.eslintExplicitGlobal) {
+ context.report({
+ node: programNode,
+ loc: getLocation(unusedVar),
+ message: getDefinedMessage(unusedVar),
+ data: unusedVar
+ });
+ } else if (unusedVar.defs.length > 0) {
+ context.report({
+ node: unusedVar.identifiers[0],
+ message: unusedVar.references.some(ref => ref.isWrite())
+ ? getAssignedMessage()
+ : getDefinedMessage(unusedVar),
+ data: unusedVar
+ });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-use-before-define.js b/tools/node_modules/eslint/lib/rules/no-use-before-define.js
new file mode 100644
index 0000000000..ada01815d9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-use-before-define.js
@@ -0,0 +1,266 @@
+/**
+ * @fileoverview Rule to flag use of variables before they are defined
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/;
+const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/;
+
+/**
+ * Parses a given value as options.
+ *
+ * @param {any} options - A value to parse.
+ * @returns {Object} The parsed options.
+ */
+function parseOptions(options) {
+ let functions = true;
+ let classes = true;
+ let variables = true;
+
+ if (typeof options === "string") {
+ functions = (options !== "nofunc");
+ } else if (typeof options === "object" && options !== null) {
+ functions = options.functions !== false;
+ classes = options.classes !== false;
+ variables = options.variables !== false;
+ }
+
+ return { functions, classes, variables };
+}
+
+/**
+ * Checks whether or not a given variable is a function declaration.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @returns {boolean} `true` if the variable is a function declaration.
+ */
+function isFunction(variable) {
+ return variable.defs[0].type === "FunctionName";
+}
+
+/**
+ * Checks whether or not a given variable is a class declaration in an upper function scope.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the variable is a class declaration.
+ */
+function isOuterClass(variable, reference) {
+ return (
+ variable.defs[0].type === "ClassName" &&
+ variable.scope.variableScope !== reference.from.variableScope
+ );
+}
+
+/**
+ * Checks whether or not a given variable is a variable declaration in an upper function scope.
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the variable is a variable declaration.
+ */
+function isOuterVariable(variable, reference) {
+ return (
+ variable.defs[0].type === "Variable" &&
+ variable.scope.variableScope !== reference.from.variableScope
+ );
+}
+
+/**
+ * Checks whether or not a given location is inside of the range of a given node.
+ *
+ * @param {ASTNode} node - An node to check.
+ * @param {number} location - A location to check.
+ * @returns {boolean} `true` if the location is inside of the range of the node.
+ */
+function isInRange(node, location) {
+ return node && node.range[0] <= location && location <= node.range[1];
+}
+
+/**
+ * Checks whether or not a given reference is inside of the initializers of a given variable.
+ *
+ * This returns `true` in the following cases:
+ *
+ * var a = a
+ * var [a = a] = list
+ * var {a = a} = obj
+ * for (var a in a) {}
+ * for (var a of a) {}
+ *
+ * @param {Variable} variable - A variable to check.
+ * @param {Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the reference is inside of the initializers.
+ */
+function isInInitializer(variable, reference) {
+ if (variable.scope !== reference.from) {
+ return false;
+ }
+
+ let node = variable.identifiers[0].parent;
+ const location = reference.identifier.range[1];
+
+ while (node) {
+ if (node.type === "VariableDeclarator") {
+ if (isInRange(node.init, location)) {
+ return true;
+ }
+ if (FOR_IN_OF_TYPE.test(node.parent.parent.type) &&
+ isInRange(node.parent.parent.right, location)
+ ) {
+ return true;
+ }
+ break;
+ } else if (node.type === "AssignmentPattern") {
+ if (isInRange(node.right, location)) {
+ return true;
+ }
+ } else if (SENTINEL_TYPE.test(node.type)) {
+ break;
+ }
+
+ node = node.parent;
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of variables before they are defined",
+ category: "Variables",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["nofunc"]
+ },
+ {
+ type: "object",
+ properties: {
+ functions: { type: "boolean" },
+ classes: { type: "boolean" },
+ variables: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const options = parseOptions(context.options[0]);
+
+ /**
+ * Determines whether a given use-before-define case should be reported according to the options.
+ * @param {eslint-scope.Variable} variable The variable that gets used before being defined
+ * @param {eslint-scope.Reference} reference The reference to the variable
+ * @returns {boolean} `true` if the usage should be reported
+ */
+ function isForbidden(variable, reference) {
+ if (isFunction(variable)) {
+ return options.functions;
+ }
+ if (isOuterClass(variable, reference)) {
+ return options.classes;
+ }
+ if (isOuterVariable(variable, reference)) {
+ return options.variables;
+ }
+ return true;
+ }
+
+ /**
+ * Finds and validates all variables in a given scope.
+ * @param {Scope} scope The scope object.
+ * @returns {void}
+ * @private
+ */
+ function findVariablesInScope(scope) {
+ scope.references.forEach(reference => {
+ const variable = reference.resolved;
+
+ /*
+ * Skips when the reference is:
+ * - initialization's.
+ * - referring to an undefined variable.
+ * - referring to a global environment variable (there're no identifiers).
+ * - located preceded by the variable (except in initializers).
+ * - allowed by options.
+ */
+ if (reference.init ||
+ !variable ||
+ variable.identifiers.length === 0 ||
+ (variable.identifiers[0].range[1] < reference.identifier.range[1] && !isInInitializer(variable, reference)) ||
+ !isForbidden(variable, reference)
+ ) {
+ return;
+ }
+
+ // Reports.
+ context.report({
+ node: reference.identifier,
+ message: "'{{name}}' was used before it was defined.",
+ data: reference.identifier
+ });
+ });
+ }
+
+ /**
+ * Validates variables inside of a node's scope.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ * @private
+ */
+ function findVariables() {
+ const scope = context.getScope();
+
+ findVariablesInScope(scope);
+ }
+
+ const ruleDefinition = {
+ "Program:exit"(node) {
+ const scope = context.getScope(),
+ ecmaFeatures = context.parserOptions.ecmaFeatures || {};
+
+ findVariablesInScope(scope);
+
+ // both Node.js and Modules have an extra scope
+ if (ecmaFeatures.globalReturn || node.sourceType === "module") {
+ findVariablesInScope(scope.childScopes[0]);
+ }
+ }
+ };
+
+ if (context.parserOptions.ecmaVersion >= 6) {
+ ruleDefinition["BlockStatement:exit"] =
+ ruleDefinition["SwitchStatement:exit"] = findVariables;
+
+ ruleDefinition["ArrowFunctionExpression:exit"] = function(node) {
+ if (node.body.type !== "BlockStatement") {
+ findVariables();
+ }
+ };
+ } else {
+ ruleDefinition["FunctionExpression:exit"] =
+ ruleDefinition["FunctionDeclaration:exit"] =
+ ruleDefinition["ArrowFunctionExpression:exit"] = findVariables;
+ }
+
+ return ruleDefinition;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-call.js b/tools/node_modules/eslint/lib/rules/no-useless-call.js
new file mode 100644
index 0000000000..e4820ac248
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-call.js
@@ -0,0 +1,80 @@
+/**
+ * @fileoverview A rule to disallow unnecessary `.call()` and `.apply()`.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a node is a `.call()`/`.apply()`.
+ * @param {ASTNode} node - A CallExpression node to check.
+ * @returns {boolean} Whether or not the node is a `.call()`/`.apply()`.
+ */
+function isCallOrNonVariadicApply(node) {
+ return (
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.computed === false &&
+ (
+ (node.callee.property.name === "call" && node.arguments.length >= 1) ||
+ (node.callee.property.name === "apply" && node.arguments.length === 2 && node.arguments[1].type === "ArrayExpression")
+ )
+ );
+}
+
+
+/**
+ * Checks whether or not `thisArg` is not changed by `.call()`/`.apply()`.
+ * @param {ASTNode|null} expectedThis - The node that is the owner of the applied function.
+ * @param {ASTNode} thisArg - The node that is given to the first argument of the `.call()`/`.apply()`.
+ * @param {SourceCode} sourceCode - The ESLint source code object.
+ * @returns {boolean} Whether or not `thisArg` is not changed by `.call()`/`.apply()`.
+ */
+function isValidThisArg(expectedThis, thisArg, sourceCode) {
+ if (!expectedThis) {
+ return astUtils.isNullOrUndefined(thisArg);
+ }
+ return astUtils.equalTokens(expectedThis, thisArg, sourceCode);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary calls to `.call()` and `.apply()`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ CallExpression(node) {
+ if (!isCallOrNonVariadicApply(node)) {
+ return;
+ }
+
+ const applied = node.callee.object;
+ const expectedThis = (applied.type === "MemberExpression") ? applied.object : null;
+ const thisArg = node.arguments[0];
+
+ if (isValidThisArg(expectedThis, thisArg, sourceCode)) {
+ context.report({ node, message: "unnecessary '.{{name}}()'.", data: { name: node.callee.property.name } });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js
new file mode 100644
index 0000000000..f8114ab754
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js
@@ -0,0 +1,75 @@
+/**
+ * @fileoverview Rule to disallow unnecessary computed property keys in object literals
+ * @author Burak Yigit Kaya
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const MESSAGE_UNNECESSARY_COMPUTED = "Unnecessarily computed property [{{property}}] found.";
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary computed property keys in object literals",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ Property(node) {
+ if (!node.computed) {
+ return;
+ }
+
+ const key = node.key,
+ nodeType = typeof key.value;
+
+ if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== "__proto__") {
+ context.report({
+ node,
+ message: MESSAGE_UNNECESSARY_COMPUTED,
+ data: { property: sourceCode.getText(key) },
+ fix(fixer) {
+ const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken);
+ const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken);
+ const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1);
+
+ if (tokensBetween.slice(0, -1).some((token, index) =>
+ sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) {
+
+ // If there are comments between the brackets and the property name, don't do a fix.
+ return null;
+ }
+
+ const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket);
+
+ // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} })
+ const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] &&
+ !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key));
+
+ const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw;
+
+ return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-concat.js b/tools/node_modules/eslint/lib/rules/no-useless-concat.js
new file mode 100644
index 0000000000..e42781fee7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-concat.js
@@ -0,0 +1,108 @@
+/**
+ * @fileoverview disallow unncessary concatenation of template strings
+ * @author Henry Zhu
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is a concatenation.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a concatenation.
+ */
+function isConcatenation(node) {
+ return node.type === "BinaryExpression" && node.operator === "+";
+}
+
+/**
+ * Checks if the given token is a `+` token or not.
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a `+` token.
+ */
+function isConcatOperatorToken(token) {
+ return token.value === "+" && token.type === "Punctuator";
+}
+
+/**
+ * Get's the right most node on the left side of a BinaryExpression with + operator.
+ * @param {ASTNode} node - A BinaryExpression node to check.
+ * @returns {ASTNode} node
+ */
+function getLeft(node) {
+ let left = node.left;
+
+ while (isConcatenation(left)) {
+ left = left.right;
+ }
+ return left;
+}
+
+/**
+ * Get's the left most node on the right side of a BinaryExpression with + operator.
+ * @param {ASTNode} node - A BinaryExpression node to check.
+ * @returns {ASTNode} node
+ */
+function getRight(node) {
+ let right = node.right;
+
+ while (isConcatenation(right)) {
+ right = right.left;
+ }
+ return right;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary concatenation of literals or template literals",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ BinaryExpression(node) {
+
+ // check if not concatenation
+ if (node.operator !== "+") {
+ return;
+ }
+
+ // account for the `foo + "a" + "b"` case
+ const left = getLeft(node);
+ const right = getRight(node);
+
+ if (astUtils.isStringLiteral(left) &&
+ astUtils.isStringLiteral(right) &&
+ astUtils.isTokenOnSameLine(left, right)
+ ) {
+ const operatorToken = sourceCode.getFirstTokenBetween(left, right, isConcatOperatorToken);
+
+ context.report({
+ node,
+ loc: operatorToken.loc.start,
+ message: "Unexpected string concatenation of literals."
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-constructor.js b/tools/node_modules/eslint/lib/rules/no-useless-constructor.js
new file mode 100644
index 0000000000..f790c789f5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-constructor.js
@@ -0,0 +1,182 @@
+/**
+ * @fileoverview Rule to flag the use of redundant constructors in classes.
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether a given array of statements is a single call of `super`.
+ *
+ * @param {ASTNode[]} body - An array of statements to check.
+ * @returns {boolean} `true` if the body is a single call of `super`.
+ */
+function isSingleSuperCall(body) {
+ return (
+ body.length === 1 &&
+ body[0].type === "ExpressionStatement" &&
+ body[0].expression.type === "CallExpression" &&
+ body[0].expression.callee.type === "Super"
+ );
+}
+
+/**
+ * Checks whether a given node is a pattern which doesn't have any side effects.
+ * Default parameters and Destructuring parameters can have side effects.
+ *
+ * @param {ASTNode} node - A pattern node.
+ * @returns {boolean} `true` if the node doesn't have any side effects.
+ */
+function isSimple(node) {
+ return node.type === "Identifier" || node.type === "RestElement";
+}
+
+/**
+ * Checks whether a given array of expressions is `...arguments` or not.
+ * `super(...arguments)` passes all arguments through.
+ *
+ * @param {ASTNode[]} superArgs - An array of expressions to check.
+ * @returns {boolean} `true` if the superArgs is `...arguments`.
+ */
+function isSpreadArguments(superArgs) {
+ return (
+ superArgs.length === 1 &&
+ superArgs[0].type === "SpreadElement" &&
+ superArgs[0].argument.type === "Identifier" &&
+ superArgs[0].argument.name === "arguments"
+ );
+}
+
+/**
+ * Checks whether given 2 nodes are identifiers which have the same name or not.
+ *
+ * @param {ASTNode} ctorParam - A node to check.
+ * @param {ASTNode} superArg - A node to check.
+ * @returns {boolean} `true` if the nodes are identifiers which have the same
+ * name.
+ */
+function isValidIdentifierPair(ctorParam, superArg) {
+ return (
+ ctorParam.type === "Identifier" &&
+ superArg.type === "Identifier" &&
+ ctorParam.name === superArg.name
+ );
+}
+
+/**
+ * Checks whether given 2 nodes are a rest/spread pair which has the same values.
+ *
+ * @param {ASTNode} ctorParam - A node to check.
+ * @param {ASTNode} superArg - A node to check.
+ * @returns {boolean} `true` if the nodes are a rest/spread pair which has the
+ * same values.
+ */
+function isValidRestSpreadPair(ctorParam, superArg) {
+ return (
+ ctorParam.type === "RestElement" &&
+ superArg.type === "SpreadElement" &&
+ isValidIdentifierPair(ctorParam.argument, superArg.argument)
+ );
+}
+
+/**
+ * Checks whether given 2 nodes have the same value or not.
+ *
+ * @param {ASTNode} ctorParam - A node to check.
+ * @param {ASTNode} superArg - A node to check.
+ * @returns {boolean} `true` if the nodes have the same value or not.
+ */
+function isValidPair(ctorParam, superArg) {
+ return (
+ isValidIdentifierPair(ctorParam, superArg) ||
+ isValidRestSpreadPair(ctorParam, superArg)
+ );
+}
+
+/**
+ * Checks whether the parameters of a constructor and the arguments of `super()`
+ * have the same values or not.
+ *
+ * @param {ASTNode} ctorParams - The parameters of a constructor to check.
+ * @param {ASTNode} superArgs - The arguments of `super()` to check.
+ * @returns {boolean} `true` if those have the same values.
+ */
+function isPassingThrough(ctorParams, superArgs) {
+ if (ctorParams.length !== superArgs.length) {
+ return false;
+ }
+
+ for (let i = 0; i < ctorParams.length; ++i) {
+ if (!isValidPair(ctorParams[i], superArgs[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Checks whether the constructor body is a redundant super call.
+ *
+ * @param {Array} body - constructor body content.
+ * @param {Array} ctorParams - The params to check against super call.
+ * @returns {boolean} true if the construtor body is redundant
+ */
+function isRedundantSuperCall(body, ctorParams) {
+ return (
+ isSingleSuperCall(body) &&
+ ctorParams.every(isSimple) &&
+ (
+ isSpreadArguments(body[0].expression.arguments) ||
+ isPassingThrough(ctorParams, body[0].expression.arguments)
+ )
+ );
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary constructors",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Checks whether a node is a redundant constructor
+ * @param {ASTNode} node - node to check
+ * @returns {void}
+ */
+ function checkForConstructor(node) {
+ if (node.kind !== "constructor") {
+ return;
+ }
+
+ const body = node.value.body.body;
+ const ctorParams = node.value.params;
+ const superClass = node.parent.parent.superClass;
+
+ if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
+ context.report({
+ node,
+ message: "Useless constructor."
+ });
+ }
+ }
+
+ return {
+ MethodDefinition: checkForConstructor
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-escape.js b/tools/node_modules/eslint/lib/rules/no-useless-escape.js
new file mode 100644
index 0000000000..cdc3e98df8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-escape.js
@@ -0,0 +1,223 @@
+/**
+ * @fileoverview Look for useless escapes in strings and regexes
+ * @author Onur Temizkan
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the union of two sets.
+ * @param {Set} setA The first set
+ * @param {Set} setB The second set
+ * @returns {Set} The union of the two sets
+ */
+function union(setA, setB) {
+ return new Set(function *() {
+ yield* setA;
+ yield* setB;
+ }());
+}
+
+const VALID_STRING_ESCAPES = union(new Set("\\nrvtbfux"), astUtils.LINEBREAKS);
+const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnrsStvwWxu0123456789]");
+const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()B"));
+
+/**
+ * Parses a regular expression into a list of characters with character class info.
+ * @param {string} regExpText The raw text used to create the regular expression
+ * @returns {Object[]} A list of characters, each with info on escaping and whether they're in a character class.
+ * @example
+ *
+ * parseRegExp('a\\b[cd-]')
+ *
+ * returns:
+ * [
+ * {text: 'a', index: 0, escaped: false, inCharClass: false, startsCharClass: false, endsCharClass: false},
+ * {text: 'b', index: 2, escaped: true, inCharClass: false, startsCharClass: false, endsCharClass: false},
+ * {text: 'c', index: 4, escaped: false, inCharClass: true, startsCharClass: true, endsCharClass: false},
+ * {text: 'd', index: 5, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false},
+ * {text: '-', index: 6, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false}
+ * ]
+ */
+function parseRegExp(regExpText) {
+ const charList = [];
+
+ regExpText.split("").reduce((state, char, index) => {
+ if (!state.escapeNextChar) {
+ if (char === "\\") {
+ return Object.assign(state, { escapeNextChar: true });
+ }
+ if (char === "[" && !state.inCharClass) {
+ return Object.assign(state, { inCharClass: true, startingCharClass: true });
+ }
+ if (char === "]" && state.inCharClass) {
+ if (charList.length && charList[charList.length - 1].inCharClass) {
+ charList[charList.length - 1].endsCharClass = true;
+ }
+ return Object.assign(state, { inCharClass: false, startingCharClass: false });
+ }
+ }
+ charList.push({
+ text: char,
+ index,
+ escaped: state.escapeNextChar,
+ inCharClass: state.inCharClass,
+ startsCharClass: state.startingCharClass,
+ endsCharClass: false
+ });
+ return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
+ }, { escapeNextChar: false, inCharClass: false, startingCharClass: false });
+
+ return charList;
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow unnecessary escape characters",
+ category: "Best Practices",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Reports a node
+ * @param {ASTNode} node The node to report
+ * @param {number} startOffset The backslash's offset from the start of the node
+ * @param {string} character The uselessly escaped character (not including the backslash)
+ * @returns {void}
+ */
+ function report(node, startOffset, character) {
+ context.report({
+ node,
+ loc: sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(node.loc.start) + startOffset),
+ message: "Unnecessary escape character: \\{{character}}.",
+ data: { character }
+ });
+ }
+
+ /**
+ * Checks if the escape character in given string slice is unnecessary.
+ *
+ * @private
+ * @param {ASTNode} node - node to validate.
+ * @param {string} match - string slice to validate.
+ * @returns {void}
+ */
+ function validateString(node, match) {
+ const isTemplateElement = node.type === "TemplateElement";
+ const escapedChar = match[0][1];
+ let isUnnecessaryEscape = !VALID_STRING_ESCAPES.has(escapedChar);
+ let isQuoteEscape;
+
+ if (isTemplateElement) {
+ isQuoteEscape = escapedChar === "`";
+
+ if (escapedChar === "$") {
+
+ // Warn if `\$` is not followed by `{`
+ isUnnecessaryEscape = match.input[match.index + 2] !== "{";
+ } else if (escapedChar === "{") {
+
+ /*
+ * Warn if `\{` is not preceded by `$`. If preceded by `$`, escaping
+ * is necessary and the rule should not warn. If preceded by `/$`, the rule
+ * will warn for the `/$` instead, as it is the first unnecessarily escaped character.
+ */
+ isUnnecessaryEscape = match.input[match.index - 1] !== "$";
+ }
+ } else {
+ isQuoteEscape = escapedChar === node.raw[0];
+ }
+
+ if (isUnnecessaryEscape && !isQuoteEscape) {
+ report(node, match.index + 1, match[0].slice(1));
+ }
+ }
+
+ /**
+ * Checks if a node has an escape.
+ *
+ * @param {ASTNode} node - node to check.
+ * @returns {void}
+ */
+ function check(node) {
+ const isTemplateElement = node.type === "TemplateElement";
+
+ if (
+ isTemplateElement &&
+ node.parent &&
+ node.parent.parent &&
+ node.parent.parent.type === "TaggedTemplateExpression" &&
+ node.parent === node.parent.parent.quasi
+ ) {
+
+ // Don't report tagged template literals, because the backslash character is accessible to the tag function.
+ return;
+ }
+
+ if (typeof node.value === "string" || isTemplateElement) {
+
+ /*
+ * JSXAttribute doesn't have any escape sequence: https://facebook.github.io/jsx/.
+ * In addition, backticks are not supported by JSX yet: https://github.com/facebook/jsx/issues/25.
+ */
+ if (node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement") {
+ return;
+ }
+
+ const value = isTemplateElement ? node.value.raw : node.raw.slice(1, -1);
+ const pattern = /\\[^\d]/g;
+ let match;
+
+ while ((match = pattern.exec(value))) {
+ validateString(node, match);
+ }
+ } else if (node.regex) {
+ parseRegExp(node.regex.pattern)
+
+ /*
+ * The '-' character is a special case, because it's only valid to escape it if it's in a character
+ * class, and is not at either edge of the character class. To account for this, don't consider '-'
+ * characters to be valid in general, and filter out '-' characters that appear in the middle of a
+ * character class.
+ */
+ .filter(charInfo => !(charInfo.text === "-" && charInfo.inCharClass && !charInfo.startsCharClass && !charInfo.endsCharClass))
+
+ /*
+ * The '^' character is also a special case; it must always be escaped outside of character classes, but
+ * it only needs to be escaped in character classes if it's at the beginning of the character class. To
+ * account for this, consider it to be a valid escape character outside of character classes, and filter
+ * out '^' characters that appear at the start of a character class.
+ */
+ .filter(charInfo => !(charInfo.text === "^" && charInfo.startsCharClass))
+
+ // Filter out characters that aren't escaped.
+ .filter(charInfo => charInfo.escaped)
+
+ // Filter out characters that are valid to escape, based on their position in the regular expression.
+ .filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
+
+ // Report all the remaining characters.
+ .forEach(charInfo => report(node, charInfo.index, charInfo.text));
+ }
+
+ }
+
+ return {
+ Literal: check,
+ TemplateElement: check
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-rename.js b/tools/node_modules/eslint/lib/rules/no-useless-rename.js
new file mode 100644
index 0000000000..a489a6e51b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-rename.js
@@ -0,0 +1,147 @@
+/**
+ * @fileoverview Disallow renaming import, export, and destructured assignments to the same name.
+ * @author Kai Cataldo
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow renaming import, export, and destructured assignments to the same name",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+ fixable: "code",
+ schema: [
+ {
+ type: "object",
+ properties: {
+ ignoreDestructuring: { type: "boolean" },
+ ignoreImport: { type: "boolean" },
+ ignoreExport: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {},
+ ignoreDestructuring = options.ignoreDestructuring === true,
+ ignoreImport = options.ignoreImport === true,
+ ignoreExport = options.ignoreExport === true;
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports error for unnecessarily renamed assignments
+ * @param {ASTNode} node - node to report
+ * @param {ASTNode} initial - node with initial name value
+ * @param {ASTNode} result - node with new name value
+ * @param {string} type - the type of the offending node
+ * @returns {void}
+ */
+ function reportError(node, initial, result, type) {
+ const name = initial.type === "Identifier" ? initial.name : initial.value;
+
+ return context.report({
+ node,
+ message: "{{type}} {{name}} unnecessarily renamed.",
+ data: {
+ name,
+ type
+ },
+ fix(fixer) {
+ return fixer.replaceTextRange([
+ initial.range[0],
+ result.range[1]
+ ], name);
+ }
+ });
+ }
+
+ /**
+ * Checks whether a destructured assignment is unnecessarily renamed
+ * @param {ASTNode} node - node to check
+ * @returns {void}
+ */
+ function checkDestructured(node) {
+ if (ignoreDestructuring) {
+ return;
+ }
+
+ const properties = node.properties;
+
+ for (let i = 0; i < properties.length; i++) {
+ if (properties[i].shorthand) {
+ continue;
+ }
+
+ /**
+ * If an ObjectPattern property is computed, we have no idea
+ * if a rename is useless or not. If an ObjectPattern property
+ * lacks a key, it is likely an ExperimentalRestProperty and
+ * so there is no "renaming" occurring here.
+ */
+ if (properties[i].computed || !properties[i].key) {
+ continue;
+ }
+
+ if (properties[i].key.type === "Identifier" && properties[i].key.name === properties[i].value.name ||
+ properties[i].key.type === "Literal" && properties[i].key.value === properties[i].value.name) {
+ reportError(properties[i], properties[i].key, properties[i].value, "Destructuring assignment");
+ }
+ }
+ }
+
+ /**
+ * Checks whether an import is unnecessarily renamed
+ * @param {ASTNode} node - node to check
+ * @returns {void}
+ */
+ function checkImport(node) {
+ if (ignoreImport) {
+ return;
+ }
+
+ if (node.imported.name === node.local.name &&
+ node.imported.range[0] !== node.local.range[0]) {
+ reportError(node, node.imported, node.local, "Import");
+ }
+ }
+
+ /**
+ * Checks whether an export is unnecessarily renamed
+ * @param {ASTNode} node - node to check
+ * @returns {void}
+ */
+ function checkExport(node) {
+ if (ignoreExport) {
+ return;
+ }
+
+ if (node.local.name === node.exported.name &&
+ node.local.range[0] !== node.exported.range[0]) {
+ reportError(node, node.local, node.exported, "Export");
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ ObjectPattern: checkDestructured,
+ ImportSpecifier: checkImport,
+ ExportSpecifier: checkExport
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-useless-return.js b/tools/node_modules/eslint/lib/rules/no-useless-return.js
new file mode 100644
index 0000000000..5415bf59b8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-useless-return.js
@@ -0,0 +1,304 @@
+/**
+ * @fileoverview Disallow redundant return statements
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils"),
+ FixTracker = require("../util/fix-tracker");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Adds all elements of 2nd argument into 1st argument.
+ *
+ * @param {Array} array - The destination array to add.
+ * @param {Array} elements - The source array to add.
+ * @returns {void}
+ */
+const pushAll = Function.apply.bind(Array.prototype.push);
+
+/**
+ * Removes the given element from the array.
+ *
+ * @param {Array} array - The source array to remove.
+ * @param {any} element - The target item to remove.
+ * @returns {void}
+ */
+function remove(array, element) {
+ const index = array.indexOf(element);
+
+ if (index !== -1) {
+ array.splice(index, 1);
+ }
+}
+
+/**
+ * Checks whether it can remove the given return statement or not.
+ *
+ * @param {ASTNode} node - The return statement node to check.
+ * @returns {boolean} `true` if the node is removeable.
+ */
+function isRemovable(node) {
+ return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
+}
+
+/**
+ * Checks whether the given return statement is in a `finally` block or not.
+ *
+ * @param {ASTNode} node - The return statement node to check.
+ * @returns {boolean} `true` if the node is in a `finally` block.
+ */
+function isInFinally(node) {
+ while (node && node.parent && !astUtils.isFunction(node)) {
+ if (node.parent.type === "TryStatement" && node.parent.finalizer === node) {
+ return true;
+ }
+
+ node = node.parent;
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow redundant return statements",
+ category: "Best Practices",
+ recommended: false
+ },
+ fixable: "code",
+ schema: []
+ },
+
+ create(context) {
+ const segmentInfoMap = new WeakMap();
+ const usedUnreachableSegments = new WeakSet();
+ let scopeInfo = null;
+
+ /**
+ * Checks whether the given segment is terminated by a return statement or not.
+ *
+ * @param {CodePathSegment} segment - The segment to check.
+ * @returns {boolean} `true` if the segment is terminated by a return statement, or if it's still a part of unreachable.
+ */
+ function isReturned(segment) {
+ const info = segmentInfoMap.get(segment);
+
+ return !info || info.returned;
+ }
+
+ /**
+ * Collects useless return statements from the given previous segments.
+ *
+ * A previous segment may be an unreachable segment.
+ * In that case, the information object of the unreachable segment is not
+ * initialized because `onCodePathSegmentStart` event is not notified for
+ * unreachable segments.
+ * This goes to the previous segments of the unreachable segment recursively
+ * if the unreachable segment was generated by a return statement. Otherwise,
+ * this ignores the unreachable segment.
+ *
+ * This behavior would simulate code paths for the case that the return
+ * statement does not exist.
+ *
+ * @param {ASTNode[]} uselessReturns - The collected return statements.
+ * @param {CodePathSegment[]} prevSegments - The previous segments to traverse.
+ * @param {WeakSet<CodePathSegment>} [traversedSegments] A set of segments that have already been traversed in this call
+ * @returns {ASTNode[]} `uselessReturns`.
+ */
+ function getUselessReturns(uselessReturns, prevSegments, traversedSegments) {
+ if (!traversedSegments) {
+ traversedSegments = new WeakSet();
+ }
+ for (const segment of prevSegments) {
+ if (!segment.reachable) {
+ if (!traversedSegments.has(segment)) {
+ traversedSegments.add(segment);
+ getUselessReturns(
+ uselessReturns,
+ segment.allPrevSegments.filter(isReturned),
+ traversedSegments
+ );
+ }
+ continue;
+ }
+
+ pushAll(uselessReturns, segmentInfoMap.get(segment).uselessReturns);
+ }
+
+ return uselessReturns;
+ }
+
+ /**
+ * Removes the return statements on the given segment from the useless return
+ * statement list.
+ *
+ * This segment may be an unreachable segment.
+ * In that case, the information object of the unreachable segment is not
+ * initialized because `onCodePathSegmentStart` event is not notified for
+ * unreachable segments.
+ * This goes to the previous segments of the unreachable segment recursively
+ * if the unreachable segment was generated by a return statement. Otherwise,
+ * this ignores the unreachable segment.
+ *
+ * This behavior would simulate code paths for the case that the return
+ * statement does not exist.
+ *
+ * @param {CodePathSegment} segment - The segment to get return statements.
+ * @returns {void}
+ */
+ function markReturnStatementsOnSegmentAsUsed(segment) {
+ if (!segment.reachable) {
+ usedUnreachableSegments.add(segment);
+ segment.allPrevSegments
+ .filter(isReturned)
+ .filter(prevSegment => !usedUnreachableSegments.has(prevSegment))
+ .forEach(markReturnStatementsOnSegmentAsUsed);
+ return;
+ }
+
+ const info = segmentInfoMap.get(segment);
+
+ for (const node of info.uselessReturns) {
+ remove(scopeInfo.uselessReturns, node);
+ }
+ info.uselessReturns = [];
+ }
+
+ /**
+ * Removes the return statements on the current segments from the useless
+ * return statement list.
+ *
+ * This function will be called at every statement except FunctionDeclaration,
+ * BlockStatement, and BreakStatement.
+ *
+ * - FunctionDeclarations are always executed whether it's returned or not.
+ * - BlockStatements do nothing.
+ * - BreakStatements go the next merely.
+ *
+ * @returns {void}
+ */
+ function markReturnStatementsOnCurrentSegmentsAsUsed() {
+ scopeInfo
+ .codePath
+ .currentSegments
+ .forEach(markReturnStatementsOnSegmentAsUsed);
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+
+ // Makes and pushs a new scope information.
+ onCodePathStart(codePath) {
+ scopeInfo = {
+ upper: scopeInfo,
+ uselessReturns: [],
+ codePath
+ };
+ },
+
+ // Reports useless return statements if exist.
+ onCodePathEnd() {
+ for (const node of scopeInfo.uselessReturns) {
+ context.report({
+ node,
+ loc: node.loc,
+ message: "Unnecessary return statement.",
+ fix(fixer) {
+ if (isRemovable(node)) {
+
+ /*
+ * Extend the replacement range to include the
+ * entire function to avoid conflicting with
+ * no-else-return.
+ * https://github.com/eslint/eslint/issues/8026
+ */
+ return new FixTracker(fixer, context.getSourceCode())
+ .retainEnclosingFunction(node)
+ .remove(node);
+ }
+ return null;
+ }
+ });
+ }
+
+ scopeInfo = scopeInfo.upper;
+ },
+
+ /*
+ * Initializes segments.
+ * NOTE: This event is notified for only reachable segments.
+ */
+ onCodePathSegmentStart(segment) {
+ const info = {
+ uselessReturns: getUselessReturns([], segment.allPrevSegments),
+ returned: false
+ };
+
+ // Stores the info.
+ segmentInfoMap.set(segment, info);
+ },
+
+ // Adds ReturnStatement node to check whether it's useless or not.
+ ReturnStatement(node) {
+ if (node.argument) {
+ markReturnStatementsOnCurrentSegmentsAsUsed();
+ }
+ if (node.argument || astUtils.isInLoop(node) || isInFinally(node)) {
+ return;
+ }
+
+ for (const segment of scopeInfo.codePath.currentSegments) {
+ const info = segmentInfoMap.get(segment);
+
+ if (info) {
+ info.uselessReturns.push(node);
+ info.returned = true;
+ }
+ }
+ scopeInfo.uselessReturns.push(node);
+ },
+
+ /*
+ * Registers for all statement nodes except FunctionDeclaration, BlockStatement, BreakStatement.
+ * Removes return statements of the current segments from the useless return statement list.
+ */
+ ClassDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ContinueStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ DebuggerStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ DoWhileStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ EmptyStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ExpressionStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ForInStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ForOfStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ForStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ IfStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ImportDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
+ LabeledStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ SwitchStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ThrowStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ TryStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ VariableDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
+ WhileStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ WithStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ExportNamedDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ExportDefaultDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
+ ExportAllDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-var.js b/tools/node_modules/eslint/lib/rules/no-var.js
new file mode 100644
index 0000000000..d3c163e557
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-var.js
@@ -0,0 +1,328 @@
+/**
+ * @fileoverview Rule to check for the usage of var.
+ * @author Jamund Ferguson
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Check whether a given variable is a global variable or not.
+ * @param {eslint-scope.Variable} variable The variable to check.
+ * @returns {boolean} `true` if the variable is a global variable.
+ */
+function isGlobal(variable) {
+ return Boolean(variable.scope) && variable.scope.type === "global";
+}
+
+/**
+ * Finds the nearest function scope or global scope walking up the scope
+ * hierarchy.
+ *
+ * @param {eslint-scope.Scope} scope - The scope to traverse.
+ * @returns {eslint-scope.Scope} a function scope or global scope containing the given
+ * scope.
+ */
+function getEnclosingFunctionScope(scope) {
+ while (scope.type !== "function" && scope.type !== "global") {
+ scope = scope.upper;
+ }
+ return scope;
+}
+
+/**
+ * Checks whether the given variable has any references from a more specific
+ * function expression (i.e. a closure).
+ *
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @returns {boolean} `true` if the variable is used from a closure.
+ */
+function isReferencedInClosure(variable) {
+ const enclosingFunctionScope = getEnclosingFunctionScope(variable.scope);
+
+ return variable.references.some(reference =>
+ getEnclosingFunctionScope(reference.from) !== enclosingFunctionScope);
+}
+
+/**
+ * Checks whether the given node is the assignee of a loop.
+ *
+ * @param {ASTNode} node - A VariableDeclaration node to check.
+ * @returns {boolean} `true` if the declaration is assigned as part of loop
+ * iteration.
+ */
+function isLoopAssignee(node) {
+ return (node.parent.type === "ForOfStatement" || node.parent.type === "ForInStatement") &&
+ node === node.parent.left;
+}
+
+/**
+ * Checks whether the given variable declaration is immediately initialized.
+ *
+ * @param {ASTNode} node - A VariableDeclaration node to check.
+ * @returns {boolean} `true` if the declaration has an initializer.
+ */
+function isDeclarationInitialized(node) {
+ return node.declarations.every(declarator => declarator.init !== null);
+}
+
+const SCOPE_NODE_TYPE = /^(?:Program|BlockStatement|SwitchStatement|ForStatement|ForInStatement|ForOfStatement)$/;
+
+/**
+ * Gets the scope node which directly contains a given node.
+ *
+ * @param {ASTNode} node - A node to get. This is a `VariableDeclaration` or
+ * an `Identifier`.
+ * @returns {ASTNode} A scope node. This is one of `Program`, `BlockStatement`,
+ * `SwitchStatement`, `ForStatement`, `ForInStatement`, and
+ * `ForOfStatement`.
+ */
+function getScopeNode(node) {
+ while (node) {
+ if (SCOPE_NODE_TYPE.test(node.type)) {
+ return node;
+ }
+
+ node = node.parent;
+ }
+
+ /* istanbul ignore next : unreachable */
+ return null;
+}
+
+/**
+ * Checks whether a given variable is redeclared or not.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @returns {boolean} `true` if the variable is redeclared.
+ */
+function isRedeclared(variable) {
+ return variable.defs.length >= 2;
+}
+
+/**
+ * Checks whether a given variable is used from outside of the specified scope.
+ *
+ * @param {ASTNode} scopeNode - A scope node to check.
+ * @returns {Function} The predicate function which checks whether a given
+ * variable is used from outside of the specified scope.
+ */
+function isUsedFromOutsideOf(scopeNode) {
+
+ /**
+ * Checks whether a given reference is inside of the specified scope or not.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to check.
+ * @returns {boolean} `true` if the reference is inside of the specified
+ * scope.
+ */
+ function isOutsideOfScope(reference) {
+ const scope = scopeNode.range;
+ const id = reference.identifier.range;
+
+ return id[0] < scope[0] || id[1] > scope[1];
+ }
+
+ return function(variable) {
+ return variable.references.some(isOutsideOfScope);
+ };
+}
+
+/**
+ * Creates the predicate function which checks whether a variable has their references in TDZ.
+ *
+ * The predicate function would return `true`:
+ *
+ * - if a reference is before the declarator. E.g. (var a = b, b = 1;)(var {a = b, b} = {};)
+ * - if a reference is in the expression of their default value. E.g. (var {a = a} = {};)
+ * - if a reference is in the expression of their initializer. E.g. (var a = a;)
+ *
+ * @param {ASTNode} node - The initializer node of VariableDeclarator.
+ * @returns {Function} The predicate function.
+ * @private
+ */
+function hasReferenceInTDZ(node) {
+ const initStart = node.range[0];
+ const initEnd = node.range[1];
+
+ return variable => {
+ const id = variable.defs[0].name;
+ const idStart = id.range[0];
+ const defaultValue = (id.parent.type === "AssignmentPattern" ? id.parent.right : null);
+ const defaultStart = defaultValue && defaultValue.range[0];
+ const defaultEnd = defaultValue && defaultValue.range[1];
+
+ return variable.references.some(reference => {
+ const start = reference.identifier.range[0];
+ const end = reference.identifier.range[1];
+
+ return !reference.init && (
+ start < idStart ||
+ (defaultValue !== null && start >= defaultStart && end <= defaultEnd) ||
+ (start >= initStart && end <= initEnd)
+ );
+ });
+ };
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `let` or `const` instead of `var`",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [],
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Checks whether the variables which are defined by the given declarator node have their references in TDZ.
+ *
+ * @param {ASTNode} declarator - The VariableDeclarator node to check.
+ * @returns {boolean} `true` if one of the variables which are defined by the given declarator node have their references in TDZ.
+ */
+ function hasSelfReferenceInTDZ(declarator) {
+ if (!declarator.init) {
+ return false;
+ }
+ const variables = context.getDeclaredVariables(declarator);
+
+ return variables.some(hasReferenceInTDZ(declarator.init));
+ }
+
+ /**
+ * Checks whether it can fix a given variable declaration or not.
+ * It cannot fix if the following cases:
+ *
+ * - A variable is a global variable.
+ * - A variable is declared on a SwitchCase node.
+ * - A variable is redeclared.
+ * - A variable is used from outside the scope.
+ * - A variable is used from a closure within a loop.
+ * - A variable might be used before it is assigned within a loop.
+ * - A variable might be used in TDZ.
+ * - A variable is declared in statement position (e.g. a single-line `IfStatement`)
+ *
+ * ## A variable is declared on a SwitchCase node.
+ *
+ * If this rule modifies 'var' declarations on a SwitchCase node, it
+ * would generate the warnings of 'no-case-declarations' rule. And the
+ * 'eslint:recommended' preset includes 'no-case-declarations' rule, so
+ * this rule doesn't modify those declarations.
+ *
+ * ## A variable is redeclared.
+ *
+ * The language spec disallows redeclarations of `let` declarations.
+ * Those variables would cause syntax errors.
+ *
+ * ## A variable is used from outside the scope.
+ *
+ * The language spec disallows accesses from outside of the scope for
+ * `let` declarations. Those variables would cause reference errors.
+ *
+ * ## A variable is used from a closure within a loop.
+ *
+ * A `var` declaration within a loop shares the same variable instance
+ * across all loop iterations, while a `let` declaration creates a new
+ * instance for each iteration. This means if a variable in a loop is
+ * referenced by any closure, changing it from `var` to `let` would
+ * change the behavior in a way that is generally unsafe.
+ *
+ * ## A variable might be used before it is assigned within a loop.
+ *
+ * Within a loop, a `let` declaration without an initializer will be
+ * initialized to null, while a `var` declaration will retain its value
+ * from the previous iteration, so it is only safe to change `var` to
+ * `let` if we can statically determine that the variable is always
+ * assigned a value before its first access in the loop body. To keep
+ * the implementation simple, we only convert `var` to `let` within
+ * loops when the variable is a loop assignee or the declaration has an
+ * initializer.
+ *
+ * @param {ASTNode} node - A variable declaration node to check.
+ * @returns {boolean} `true` if it can fix the node.
+ */
+ function canFix(node) {
+ const variables = context.getDeclaredVariables(node);
+ const scopeNode = getScopeNode(node);
+
+ if (node.parent.type === "SwitchCase" ||
+ node.declarations.some(hasSelfReferenceInTDZ) ||
+ variables.some(isGlobal) ||
+ variables.some(isRedeclared) ||
+ variables.some(isUsedFromOutsideOf(scopeNode))
+ ) {
+ return false;
+ }
+
+ if (astUtils.isInLoop(node)) {
+ if (variables.some(isReferencedInClosure)) {
+ return false;
+ }
+ if (!isLoopAssignee(node) && !isDeclarationInitialized(node)) {
+ return false;
+ }
+ }
+
+ if (
+ !isLoopAssignee(node) &&
+ !(node.parent.type === "ForStatement" && node.parent.init === node) &&
+ !astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)
+ ) {
+
+ // If the declaration is not in a block, e.g. `if (foo) var bar = 1;`, then it can't be fixed.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Reports a given variable declaration node.
+ *
+ * @param {ASTNode} node - A variable declaration node to report.
+ * @returns {void}
+ */
+ function report(node) {
+ const varToken = sourceCode.getFirstToken(node);
+
+ context.report({
+ node,
+ message: "Unexpected var, use let or const instead.",
+
+ fix(fixer) {
+ if (canFix(node)) {
+ return fixer.replaceText(varToken, "let");
+ }
+ return null;
+ }
+ });
+ }
+
+ return {
+ "VariableDeclaration:exit"(node) {
+ if (node.kind === "var") {
+ report(node);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-void.js b/tools/node_modules/eslint/lib/rules/no-void.js
new file mode 100644
index 0000000000..5202fa49a8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-void.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Rule to disallow use of void operator.
+ * @author Mike Sidorov
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `void` operators",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ UnaryExpression(node) {
+ if (node.operator === "void") {
+ context.report({ node, message: "Expected 'undefined' and instead saw 'void'." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-warning-comments.js b/tools/node_modules/eslint/lib/rules/no-warning-comments.js
new file mode 100644
index 0000000000..c0ecaca9e7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-warning-comments.js
@@ -0,0 +1,139 @@
+/**
+ * @fileoverview Rule that warns about used warning comments
+ * @author Alexander Schmidt <https://github.com/lxanders>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow specified warning terms in comments",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ terms: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ location: {
+ enum: ["start", "anywhere"]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode(),
+ configuration = context.options[0] || {},
+ warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
+ location = configuration.location || "start",
+ selfConfigRegEx = /\bno-warning-comments\b/;
+
+ /**
+ * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified
+ * location ("start" or "anywhere"). If the term starts or ends with non word characters, then the match will not
+ * require word boundaries on that side.
+ *
+ * @param {string} term A term to convert to a RegExp
+ * @returns {RegExp} The term converted to a RegExp
+ */
+ function convertToRegExp(term) {
+ const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/g, "\\$&");
+ let prefix;
+
+ /*
+ * If the term ends in a word character (a-z0-9_), ensure a word
+ * boundary at the end, so that substrings do not get falsely
+ * matched. eg "todo" in a string such as "mastodon".
+ * If the term ends in a non-word character, then \b won't match on
+ * the boundary to the next non-word character, which would likely
+ * be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`.
+ * In these cases, use no bounding match. Same applies for the
+ * prefix, handled below.
+ */
+ const suffix = /\w$/.test(term) ? "\\b" : "";
+
+ if (location === "start") {
+
+ /*
+ * When matching at the start, ignore leading whitespace, and
+ * there's no need to worry about word boundaries.
+ */
+ prefix = "^\\s*";
+ } else if (/^\w/.test(term)) {
+ prefix = "\\b";
+ } else {
+ prefix = "";
+ }
+
+ return new RegExp(prefix + escaped + suffix, "i");
+ }
+
+ const warningRegExps = warningTerms.map(convertToRegExp);
+
+ /**
+ * Checks the specified comment for matches of the configured warning terms and returns the matches.
+ * @param {string} comment The comment which is checked.
+ * @returns {Array} All matched warning terms for this comment.
+ */
+ function commentContainsWarningTerm(comment) {
+ const matches = [];
+
+ warningRegExps.forEach((regex, index) => {
+ if (regex.test(comment)) {
+ matches.push(warningTerms[index]);
+ }
+ });
+
+ return matches;
+ }
+
+ /**
+ * Checks the specified node for matching warning comments and reports them.
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {void} undefined.
+ */
+ function checkComment(node) {
+ if (astUtils.isDirectiveComment(node) && selfConfigRegEx.test(node.value)) {
+ return;
+ }
+
+ const matches = commentContainsWarningTerm(node.value);
+
+ matches.forEach(matchedTerm => {
+ context.report({
+ node,
+ message: "Unexpected '{{matchedTerm}}' comment.",
+ data: {
+ matchedTerm
+ }
+ });
+ });
+ }
+
+ return {
+ Program() {
+ const comments = sourceCode.getAllComments();
+
+ comments.filter(token => token.type !== "Shebang").forEach(checkComment);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-whitespace-before-property.js b/tools/node_modules/eslint/lib/rules/no-whitespace-before-property.js
new file mode 100644
index 0000000000..2d476b66c0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-whitespace-before-property.js
@@ -0,0 +1,94 @@
+/**
+ * @fileoverview Rule to disallow whitespace before properties
+ * @author Kai Cataldo
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow whitespace before properties",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports whitespace before property token
+ * @param {ASTNode} node - the node to report in the event of an error
+ * @param {Token} leftToken - the left token
+ * @param {Token} rightToken - the right token
+ * @returns {void}
+ * @private
+ */
+ function reportError(node, leftToken, rightToken) {
+ const replacementText = node.computed ? "" : ".";
+
+ context.report({
+ node,
+ message: "Unexpected whitespace before property {{propName}}.",
+ data: {
+ propName: sourceCode.getText(node.property)
+ },
+ fix(fixer) {
+ if (!node.computed && astUtils.isDecimalInteger(node.object)) {
+
+ /*
+ * If the object is a number literal, fixing it to something like 5.toString() would cause a SyntaxError.
+ * Don't fix this case.
+ */
+ return null;
+ }
+ return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], replacementText);
+ }
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ MemberExpression(node) {
+ let rightToken;
+ let leftToken;
+
+ if (!astUtils.isTokenOnSameLine(node.object, node.property)) {
+ return;
+ }
+
+ if (node.computed) {
+ rightToken = sourceCode.getTokenBefore(node.property, astUtils.isOpeningBracketToken);
+ leftToken = sourceCode.getTokenBefore(rightToken);
+ } else {
+ rightToken = sourceCode.getFirstToken(node.property);
+ leftToken = sourceCode.getTokenBefore(rightToken, 1);
+ }
+
+ if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) {
+ reportError(node, leftToken, rightToken);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/no-with.js b/tools/node_modules/eslint/lib/rules/no-with.js
new file mode 100644
index 0000000000..be9e346360
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-with.js
@@ -0,0 +1,32 @@
+/**
+ * @fileoverview Rule to flag use of with statement
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `with` statements",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ WithStatement(node) {
+ context.report({ node, message: "Unexpected use of 'with' statement." });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/nonblock-statement-body-position.js b/tools/node_modules/eslint/lib/rules/nonblock-statement-body-position.js
new file mode 100644
index 0000000000..212e36a57c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/nonblock-statement-body-position.js
@@ -0,0 +1,114 @@
+/**
+ * @fileoverview enforce the location of single-line statements
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const POSITION_SCHEMA = { enum: ["beside", "below", "any"] };
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the location of single-line statements",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ POSITION_SCHEMA,
+ {
+ properties: {
+ overrides: {
+ properties: {
+ if: POSITION_SCHEMA,
+ else: POSITION_SCHEMA,
+ while: POSITION_SCHEMA,
+ do: POSITION_SCHEMA,
+ for: POSITION_SCHEMA
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Gets the applicable preference for a particular keyword
+ * @param {string} keywordName The name of a keyword, e.g. 'if'
+ * @returns {string} The applicable option for the keyword, e.g. 'beside'
+ */
+ function getOption(keywordName) {
+ return context.options[1] && context.options[1].overrides && context.options[1].overrides[keywordName] ||
+ context.options[0] ||
+ "beside";
+ }
+
+ /**
+ * Validates the location of a single-line statement
+ * @param {ASTNode} node The single-line statement
+ * @param {string} keywordName The applicable keyword name for the single-line statement
+ * @returns {void}
+ */
+ function validateStatement(node, keywordName) {
+ const option = getOption(keywordName);
+
+ if (node.type === "BlockStatement" || option === "any") {
+ return;
+ }
+
+ const tokenBefore = sourceCode.getTokenBefore(node);
+
+ if (tokenBefore.loc.end.line === node.loc.start.line && option === "below") {
+ context.report({
+ node,
+ message: "Expected a linebreak before this statement.",
+ fix: fixer => fixer.insertTextBefore(node, "\n")
+ });
+ } else if (tokenBefore.loc.end.line !== node.loc.start.line && option === "beside") {
+ context.report({
+ node,
+ message: "Expected no linebreak before this statement.",
+ fix(fixer) {
+ if (sourceCode.getText().slice(tokenBefore.range[1], node.range[0]).trim()) {
+ return null;
+ }
+ return fixer.replaceTextRange([tokenBefore.range[1], node.range[0]], " ");
+ }
+ });
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+ IfStatement(node) {
+ validateStatement(node.consequent, "if");
+
+ // Check the `else` node, but don't check 'else if' statements.
+ if (node.alternate && node.alternate.type !== "IfStatement") {
+ validateStatement(node.alternate, "else");
+ }
+ },
+ WhileStatement: node => validateStatement(node.body, "while"),
+ DoWhileStatement: node => validateStatement(node.body, "do"),
+ ForStatement: node => validateStatement(node.body, "for"),
+ ForInStatement: node => validateStatement(node.body, "for"),
+ ForOfStatement: node => validateStatement(node.body, "for")
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/object-curly-newline.js b/tools/node_modules/eslint/lib/rules/object-curly-newline.js
new file mode 100644
index 0000000000..ebad69de2e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/object-curly-newline.js
@@ -0,0 +1,249 @@
+/**
+ * @fileoverview Rule to require or disallow line breaks inside braces.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+// Schema objects.
+const OPTION_VALUE = {
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ multiline: {
+ type: "boolean"
+ },
+ minProperties: {
+ type: "integer",
+ minimum: 0
+ },
+ consistent: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false,
+ minProperties: 1
+ }
+ ]
+};
+
+/**
+ * Normalizes a given option value.
+ *
+ * @param {string|Object|undefined} value - An option value to parse.
+ * @returns {{multiline: boolean, minProperties: number, consistent: boolean}} Normalized option object.
+ */
+function normalizeOptionValue(value) {
+ let multiline = false;
+ let minProperties = Number.POSITIVE_INFINITY;
+ let consistent = false;
+
+ if (value) {
+ if (value === "always") {
+ minProperties = 0;
+ } else if (value === "never") {
+ minProperties = Number.POSITIVE_INFINITY;
+ } else {
+ multiline = Boolean(value.multiline);
+ minProperties = value.minProperties || Number.POSITIVE_INFINITY;
+ consistent = Boolean(value.consistent);
+ }
+ } else {
+ multiline = true;
+ }
+
+ return { multiline, minProperties, consistent };
+}
+
+/**
+ * Normalizes a given option value.
+ *
+ * @param {string|Object|undefined} options - An option value to parse.
+ * @returns {{ObjectExpression: {multiline: boolean, minProperties: number}, ObjectPattern: {multiline: boolean, minProperties: number}}} Normalized option object.
+ */
+function normalizeOptions(options) {
+ if (options && (options.ObjectExpression || options.ObjectPattern)) {
+ return {
+ ObjectExpression: normalizeOptionValue(options.ObjectExpression),
+ ObjectPattern: normalizeOptionValue(options.ObjectPattern)
+ };
+ }
+
+ const value = normalizeOptionValue(options);
+
+ return { ObjectExpression: value, ObjectPattern: value };
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent line breaks inside braces",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ oneOf: [
+ OPTION_VALUE,
+ {
+ type: "object",
+ properties: {
+ ObjectExpression: OPTION_VALUE,
+ ObjectPattern: OPTION_VALUE
+ },
+ additionalProperties: false,
+ minProperties: 1
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const normalizedOptions = normalizeOptions(context.options[0]);
+
+ /**
+ * Reports a given node if it violated this rule.
+ *
+ * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
+ * @param {{multiline: boolean, minProperties: number}} options - An option object.
+ * @returns {void}
+ */
+ function check(node) {
+ const options = normalizedOptions[node.type];
+ const openBrace = sourceCode.getFirstToken(node, token => token.value === "{");
+ let closeBrace;
+
+ if (node.typeAnnotation) {
+ closeBrace = sourceCode.getTokenBefore(node.typeAnnotation);
+ } else {
+ closeBrace = sourceCode.getLastToken(node);
+ }
+
+ let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
+ let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
+ const needsLinebreaks = (
+ node.properties.length >= options.minProperties ||
+ (
+ options.multiline &&
+ node.properties.length > 0 &&
+ first.loc.start.line !== last.loc.end.line
+ )
+ );
+ const hasCommentsFirstToken = astUtils.isCommentToken(first);
+ const hasCommentsLastToken = astUtils.isCommentToken(last);
+
+ /*
+ * Use tokens or comments to check multiline or not.
+ * But use only tokens to check whether line breaks are needed.
+ * This allows:
+ * var obj = { // eslint-disable-line foo
+ * a: 1
+ * }
+ */
+ first = sourceCode.getTokenAfter(openBrace);
+ last = sourceCode.getTokenBefore(closeBrace);
+
+ if (needsLinebreaks) {
+ if (astUtils.isTokenOnSameLine(openBrace, first)) {
+ context.report({
+ message: "Expected a line break after this opening brace.",
+ node,
+ loc: openBrace.loc.start,
+ fix(fixer) {
+ if (hasCommentsFirstToken) {
+ return null;
+ }
+
+ return fixer.insertTextAfter(openBrace, "\n");
+ }
+ });
+ }
+ if (astUtils.isTokenOnSameLine(last, closeBrace)) {
+ context.report({
+ message: "Expected a line break before this closing brace.",
+ node,
+ loc: closeBrace.loc.start,
+ fix(fixer) {
+ if (hasCommentsLastToken) {
+ return null;
+ }
+
+ return fixer.insertTextBefore(closeBrace, "\n");
+ }
+ });
+ }
+ } else {
+ const consistent = options.consistent;
+ const hasLineBreakBetweenOpenBraceAndFirst = !astUtils.isTokenOnSameLine(openBrace, first);
+ const hasLineBreakBetweenCloseBraceAndLast = !astUtils.isTokenOnSameLine(last, closeBrace);
+
+ if (
+ (!consistent && hasLineBreakBetweenOpenBraceAndFirst) ||
+ (consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast)
+ ) {
+ context.report({
+ message: "Unexpected line break after this opening brace.",
+ node,
+ loc: openBrace.loc.start,
+ fix(fixer) {
+ if (hasCommentsFirstToken) {
+ return null;
+ }
+
+ return fixer.removeRange([
+ openBrace.range[1],
+ first.range[0]
+ ]);
+ }
+ });
+ }
+ if (
+ (!consistent && hasLineBreakBetweenCloseBraceAndLast) ||
+ (consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast)
+ ) {
+ context.report({
+ message: "Unexpected line break before this closing brace.",
+ node,
+ loc: closeBrace.loc.start,
+ fix(fixer) {
+ if (hasCommentsLastToken) {
+ return null;
+ }
+
+ return fixer.removeRange([
+ last.range[1],
+ closeBrace.range[0]
+ ]);
+ }
+ });
+ }
+ }
+ }
+
+ return {
+ ObjectExpression: check,
+ ObjectPattern: check
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js
new file mode 100644
index 0000000000..3341e915f2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js
@@ -0,0 +1,299 @@
+/**
+ * @fileoverview Disallows or enforces spaces inside of object literals.
+ * @author Jamund Ferguson
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing inside braces",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ arraysInObjects: {
+ type: "boolean"
+ },
+ objectsInObjects: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const spaced = context.options[0] === "always",
+ sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether an option is set, relative to the spacing option.
+ * If spaced is "always", then check whether option is set to false.
+ * If spaced is "never", then check whether option is set to true.
+ * @param {Object} option - The option to exclude.
+ * @returns {boolean} Whether or not the property is excluded.
+ */
+ function isOptionSet(option) {
+ return context.options[1] ? context.options[1][option] === !spaced : false;
+ }
+
+ const options = {
+ spaced,
+ arraysInObjectsException: isOptionSet("arraysInObjects"),
+ objectsInObjectsException: isOptionSet("objectsInObjects")
+ };
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports that there shouldn't be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoBeginningSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "There should be no space after '{{token}}'.",
+ data: {
+ token: token.value
+ },
+ fix(fixer) {
+ const nextToken = context.getSourceCode().getTokenAfter(token);
+
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoEndingSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "There should be no space before '{{token}}'.",
+ data: {
+ token: token.value
+ },
+ fix(fixer) {
+ const previousToken = context.getSourceCode().getTokenBefore(token);
+
+ return fixer.removeRange([previousToken.range[1], token.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredBeginningSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "A space is required after '{{token}}'.",
+ data: {
+ token: token.value
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredEndingSpace(node, token) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: "A space is required before '{{token}}'.",
+ data: {
+ token: token.value
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ });
+ }
+
+ /**
+ * Determines if spacing in curly braces is valid.
+ * @param {ASTNode} node The AST node to check.
+ * @param {Token} first The first token to check (should be the opening brace)
+ * @param {Token} second The second token to check (should be first after the opening brace)
+ * @param {Token} penultimate The penultimate token to check (should be last before closing brace)
+ * @param {Token} last The last token to check (should be closing brace)
+ * @returns {void}
+ */
+ function validateBraceSpacing(node, first, second, penultimate, last) {
+ if (astUtils.isTokenOnSameLine(first, second)) {
+ const firstSpaced = sourceCode.isSpaceBetweenTokens(first, second);
+
+ if (options.spaced && !firstSpaced) {
+ reportRequiredBeginningSpace(node, first);
+ }
+ if (!options.spaced && firstSpaced) {
+ reportNoBeginningSpace(node, first);
+ }
+ }
+
+ if (astUtils.isTokenOnSameLine(penultimate, last)) {
+ const shouldCheckPenultimate = (
+ options.arraysInObjectsException && astUtils.isClosingBracketToken(penultimate) ||
+ options.objectsInObjectsException && astUtils.isClosingBraceToken(penultimate)
+ );
+ const penultimateType = shouldCheckPenultimate && sourceCode.getNodeByRangeIndex(penultimate.range[0]).type;
+
+ const closingCurlyBraceMustBeSpaced = (
+ options.arraysInObjectsException && penultimateType === "ArrayExpression" ||
+ options.objectsInObjectsException && (penultimateType === "ObjectExpression" || penultimateType === "ObjectPattern")
+ ) ? !options.spaced : options.spaced;
+
+ const lastSpaced = sourceCode.isSpaceBetweenTokens(penultimate, last);
+
+ if (closingCurlyBraceMustBeSpaced && !lastSpaced) {
+ reportRequiredEndingSpace(node, last);
+ }
+ if (!closingCurlyBraceMustBeSpaced && lastSpaced) {
+ reportNoEndingSpace(node, last);
+ }
+ }
+ }
+
+ /**
+ * Gets '}' token of an object node.
+ *
+ * Because the last token of object patterns might be a type annotation,
+ * this traverses tokens preceded by the last property, then returns the
+ * first '}' token.
+ *
+ * @param {ASTNode} node - The node to get. This node is an
+ * ObjectExpression or an ObjectPattern. And this node has one or
+ * more properties.
+ * @returns {Token} '}' token.
+ */
+ function getClosingBraceOfObject(node) {
+ const lastProperty = node.properties[node.properties.length - 1];
+
+ return sourceCode.getTokenAfter(lastProperty, astUtils.isClosingBraceToken);
+ }
+
+ /**
+ * Reports a given object node if spacing in curly braces is invalid.
+ * @param {ASTNode} node - An ObjectExpression or ObjectPattern node to check.
+ * @returns {void}
+ */
+ function checkForObject(node) {
+ if (node.properties.length === 0) {
+ return;
+ }
+
+ const first = sourceCode.getFirstToken(node),
+ last = getClosingBraceOfObject(node),
+ second = sourceCode.getTokenAfter(first),
+ penultimate = sourceCode.getTokenBefore(last);
+
+ validateBraceSpacing(node, first, second, penultimate, last);
+ }
+
+ /**
+ * Reports a given import node if spacing in curly braces is invalid.
+ * @param {ASTNode} node - An ImportDeclaration node to check.
+ * @returns {void}
+ */
+ function checkForImport(node) {
+ if (node.specifiers.length === 0) {
+ return;
+ }
+
+ let firstSpecifier = node.specifiers[0];
+ const lastSpecifier = node.specifiers[node.specifiers.length - 1];
+
+ if (lastSpecifier.type !== "ImportSpecifier") {
+ return;
+ }
+ if (firstSpecifier.type !== "ImportSpecifier") {
+ firstSpecifier = node.specifiers[1];
+ }
+
+ const first = sourceCode.getTokenBefore(firstSpecifier),
+ last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
+ second = sourceCode.getTokenAfter(first),
+ penultimate = sourceCode.getTokenBefore(last);
+
+ validateBraceSpacing(node, first, second, penultimate, last);
+ }
+
+ /**
+ * Reports a given export node if spacing in curly braces is invalid.
+ * @param {ASTNode} node - An ExportNamedDeclaration node to check.
+ * @returns {void}
+ */
+ function checkForExport(node) {
+ if (node.specifiers.length === 0) {
+ return;
+ }
+
+ const firstSpecifier = node.specifiers[0],
+ lastSpecifier = node.specifiers[node.specifiers.length - 1],
+ first = sourceCode.getTokenBefore(firstSpecifier),
+ last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
+ second = sourceCode.getTokenAfter(first),
+ penultimate = sourceCode.getTokenBefore(last);
+
+ validateBraceSpacing(node, first, second, penultimate, last);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ // var {x} = y;
+ ObjectPattern: checkForObject,
+
+ // var y = {x: 'y'}
+ ObjectExpression: checkForObject,
+
+ // import {y} from 'x';
+ ImportDeclaration: checkForImport,
+
+ // export {name} from 'yo';
+ ExportNamedDeclaration: checkForExport
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/object-property-newline.js b/tools/node_modules/eslint/lib/rules/object-property-newline.js
new file mode 100644
index 0000000000..0463e389ab
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/object-property-newline.js
@@ -0,0 +1,84 @@
+/**
+ * @fileoverview Rule to enforce placing object properties on separate lines.
+ * @author Vitor Balocco
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce placing object properties on separate lines",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowMultiplePropertiesPerLine: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "whitespace"
+ },
+
+ create(context) {
+ const allowSameLine = context.options[0] && Boolean(context.options[0].allowMultiplePropertiesPerLine);
+ const errorMessage = allowSameLine
+ ? "Object properties must go on a new line if they aren't all on the same line."
+ : "Object properties must go on a new line.";
+
+ const sourceCode = context.getSourceCode();
+
+ return {
+ ObjectExpression(node) {
+ if (allowSameLine) {
+ if (node.properties.length > 1) {
+ const firstTokenOfFirstProperty = sourceCode.getFirstToken(node.properties[0]);
+ const lastTokenOfLastProperty = sourceCode.getLastToken(node.properties[node.properties.length - 1]);
+
+ if (firstTokenOfFirstProperty.loc.end.line === lastTokenOfLastProperty.loc.start.line) {
+
+ // All keys and values are on the same line
+ return;
+ }
+ }
+ }
+
+ for (let i = 1; i < node.properties.length; i++) {
+ const lastTokenOfPreviousProperty = sourceCode.getLastToken(node.properties[i - 1]);
+ const firstTokenOfCurrentProperty = sourceCode.getFirstToken(node.properties[i]);
+
+ if (lastTokenOfPreviousProperty.loc.end.line === firstTokenOfCurrentProperty.loc.start.line) {
+ context.report({
+ node,
+ loc: firstTokenOfCurrentProperty.loc.start,
+ message: errorMessage,
+ fix(fixer) {
+ const comma = sourceCode.getTokenBefore(firstTokenOfCurrentProperty);
+ const rangeAfterComma = [comma.range[1], firstTokenOfCurrentProperty.range[0]];
+
+ // Don't perform a fix if there are any comments between the comma and the next property.
+ if (sourceCode.text.slice(rangeAfterComma[0], rangeAfterComma[1]).trim()) {
+ return null;
+ }
+
+ return fixer.replaceTextRange(rangeAfterComma, "\n");
+ }
+ });
+ }
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/object-shorthand.js b/tools/node_modules/eslint/lib/rules/object-shorthand.js
new file mode 100644
index 0000000000..980d0fc35a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/object-shorthand.js
@@ -0,0 +1,454 @@
+/**
+ * @fileoverview Rule to enforce concise object methods and properties.
+ * @author Jamund Ferguson
+ */
+
+"use strict";
+
+const OPTIONS = {
+ always: "always",
+ never: "never",
+ methods: "methods",
+ properties: "properties",
+ consistent: "consistent",
+ consistentAsNeeded: "consistent-as-needed"
+};
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow method and property shorthand syntax for object literals",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always", "methods", "properties", "never", "consistent", "consistent-as-needed"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always", "methods", "properties"]
+ },
+ {
+ type: "object",
+ properties: {
+ avoidQuotes: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always", "methods"]
+ },
+ {
+ type: "object",
+ properties: {
+ ignoreConstructors: {
+ type: "boolean"
+ },
+ avoidQuotes: {
+ type: "boolean"
+ },
+ avoidExplicitReturnArrows: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ }
+ },
+
+ create(context) {
+ const APPLY = context.options[0] || OPTIONS.always;
+ const APPLY_TO_METHODS = APPLY === OPTIONS.methods || APPLY === OPTIONS.always;
+ const APPLY_TO_PROPS = APPLY === OPTIONS.properties || APPLY === OPTIONS.always;
+ const APPLY_NEVER = APPLY === OPTIONS.never;
+ const APPLY_CONSISTENT = APPLY === OPTIONS.consistent;
+ const APPLY_CONSISTENT_AS_NEEDED = APPLY === OPTIONS.consistentAsNeeded;
+
+ const PARAMS = context.options[1] || {};
+ const IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors;
+ const AVOID_QUOTES = PARAMS.avoidQuotes;
+ const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Determines if the first character of the name is a capital letter.
+ * @param {string} name The name of the node to evaluate.
+ * @returns {boolean} True if the first character of the property name is a capital letter, false if not.
+ * @private
+ */
+ function isConstructor(name) {
+ const firstChar = name.charAt(0);
+
+ return firstChar === firstChar.toUpperCase();
+ }
+
+ /**
+ * Determines if the property can have a shorthand form.
+ * @param {ASTNode} property Property AST node
+ * @returns {boolean} True if the property can have a shorthand form
+ * @private
+ *
+ */
+ function canHaveShorthand(property) {
+ return (property.kind !== "set" && property.kind !== "get" && property.type !== "SpreadProperty" && property.type !== "ExperimentalSpreadProperty");
+ }
+
+ /**
+ * Checks whether a node is a string literal.
+ * @param {ASTNode} node - Any AST node.
+ * @returns {boolean} `true` if it is a string literal.
+ */
+ function isStringLiteral(node) {
+ return node.type === "Literal" && typeof node.value === "string";
+ }
+
+ /**
+ * Determines if the property is a shorthand or not.
+ * @param {ASTNode} property Property AST node
+ * @returns {boolean} True if the property is considered shorthand, false if not.
+ * @private
+ *
+ */
+ function isShorthand(property) {
+
+ // property.method is true when `{a(){}}`.
+ return (property.shorthand || property.method);
+ }
+
+ /**
+ * Determines if the property's key and method or value are named equally.
+ * @param {ASTNode} property Property AST node
+ * @returns {boolean} True if the key and value are named equally, false if not.
+ * @private
+ *
+ */
+ function isRedundant(property) {
+ const value = property.value;
+
+ if (value.type === "FunctionExpression") {
+ return !value.id; // Only anonymous should be shorthand method.
+ }
+ if (value.type === "Identifier") {
+ return astUtils.getStaticPropertyName(property) === value.name;
+ }
+
+ return false;
+ }
+
+ /**
+ * Ensures that an object's properties are consistently shorthand, or not shorthand at all.
+ * @param {ASTNode} node Property AST node
+ * @param {boolean} checkRedundancy Whether to check longform redundancy
+ * @returns {void}
+ *
+ */
+ function checkConsistency(node, checkRedundancy) {
+
+ // We are excluding getters/setters and spread properties as they are considered neither longform nor shorthand.
+ const properties = node.properties.filter(canHaveShorthand);
+
+ // Do we still have properties left after filtering the getters and setters?
+ if (properties.length > 0) {
+ const shorthandProperties = properties.filter(isShorthand);
+
+ /*
+ * If we do not have an equal number of longform properties as
+ * shorthand properties, we are using the annotations inconsistently
+ */
+ if (shorthandProperties.length !== properties.length) {
+
+ // We have at least 1 shorthand property
+ if (shorthandProperties.length > 0) {
+ context.report({ node, message: "Unexpected mix of shorthand and non-shorthand properties." });
+ } else if (checkRedundancy) {
+
+ /*
+ * If all properties of the object contain a method or value with a name matching it's key,
+ * all the keys are redundant.
+ */
+ const canAlwaysUseShorthand = properties.every(isRedundant);
+
+ if (canAlwaysUseShorthand) {
+ context.report({ node, message: "Expected shorthand for all properties." });
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Fixes a FunctionExpression node by making it into a shorthand property.
+ * @param {SourceCodeFixer} fixer The fixer object
+ * @param {ASTNode} node A `Property` node that has a `FunctionExpression` or `ArrowFunctionExpression` as its value
+ * @returns {Object} A fix for this node
+ */
+ function makeFunctionShorthand(fixer, node) {
+ const firstKeyToken = node.computed
+ ? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken)
+ : sourceCode.getFirstToken(node.key);
+ const lastKeyToken = node.computed
+ ? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken)
+ : sourceCode.getLastToken(node.key);
+ const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
+ let keyPrefix = "";
+
+ if (node.value.generator) {
+ keyPrefix = "*";
+ } else if (node.value.async) {
+ keyPrefix = "async ";
+ }
+
+ if (node.value.type === "FunctionExpression") {
+ const functionToken = sourceCode.getTokens(node.value).find(token => token.type === "Keyword" && token.value === "function");
+ const tokenBeforeParams = node.value.generator ? sourceCode.getTokenAfter(functionToken) : functionToken;
+
+ return fixer.replaceTextRange(
+ [firstKeyToken.range[0], node.range[1]],
+ keyPrefix + keyText + sourceCode.text.slice(tokenBeforeParams.range[1], node.value.range[1])
+ );
+ }
+ const arrowToken = sourceCode.getTokens(node.value).find(token => token.value === "=>");
+ const tokenBeforeArrow = sourceCode.getTokenBefore(arrowToken);
+ const hasParensAroundParameters = tokenBeforeArrow.type === "Punctuator" && tokenBeforeArrow.value === ")";
+ const oldParamText = sourceCode.text.slice(sourceCode.getFirstToken(node.value, node.value.async ? 1 : 0).range[0], tokenBeforeArrow.range[1]);
+ const newParamText = hasParensAroundParameters ? oldParamText : `(${oldParamText})`;
+
+ return fixer.replaceTextRange(
+ [firstKeyToken.range[0], node.range[1]],
+ keyPrefix + keyText + newParamText + sourceCode.text.slice(arrowToken.range[1], node.value.range[1])
+ );
+
+ }
+
+ /**
+ * Fixes a FunctionExpression node by making it into a longform property.
+ * @param {SourceCodeFixer} fixer The fixer object
+ * @param {ASTNode} node A `Property` node that has a `FunctionExpression` as its value
+ * @returns {Object} A fix for this node
+ */
+ function makeFunctionLongform(fixer, node) {
+ const firstKeyToken = node.computed ? sourceCode.getTokens(node).find(token => token.value === "[") : sourceCode.getFirstToken(node.key);
+ const lastKeyToken = node.computed ? sourceCode.getTokensBetween(node.key, node.value).find(token => token.value === "]") : sourceCode.getLastToken(node.key);
+ const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
+ let functionHeader = "function";
+
+ if (node.value.generator) {
+ functionHeader = "function*";
+ } else if (node.value.async) {
+ functionHeader = "async function";
+ }
+
+ return fixer.replaceTextRange([node.range[0], lastKeyToken.range[1]], `${keyText}: ${functionHeader}`);
+ }
+
+ /*
+ * To determine whether a given arrow function has a lexical identifier (`this`, `arguments`, `super`, or `new.target`),
+ * create a stack of functions that define these identifiers (i.e. all functions except arrow functions) as the AST is
+ * traversed. Whenever a new function is encountered, create a new entry on the stack (corresponding to a different lexical
+ * scope of `this`), and whenever a function is exited, pop that entry off the stack. When an arrow function is entered,
+ * keep a reference to it on the current stack entry, and remove that reference when the arrow function is exited.
+ * When a lexical identifier is encountered, mark all the arrow functions on the current stack entry by adding them
+ * to an `arrowsWithLexicalIdentifiers` set. Any arrow function in that set will not be reported by this rule,
+ * because converting it into a method would change the value of one of the lexical identifiers.
+ */
+ const lexicalScopeStack = [];
+ const arrowsWithLexicalIdentifiers = new WeakSet();
+ const argumentsIdentifiers = new WeakSet();
+
+ /**
+ * Enters a function. This creates a new lexical identifier scope, so a new Set of arrow functions is pushed onto the stack.
+ * Also, this marks all `arguments` identifiers so that they can be detected later.
+ * @returns {void}
+ */
+ function enterFunction() {
+ lexicalScopeStack.unshift(new Set());
+ context.getScope().variables.filter(variable => variable.name === "arguments").forEach(variable => {
+ variable.references.map(ref => ref.identifier).forEach(identifier => argumentsIdentifiers.add(identifier));
+ });
+ }
+
+ /**
+ * Exits a function. This pops the current set of arrow functions off the lexical scope stack.
+ * @returns {void}
+ */
+ function exitFunction() {
+ lexicalScopeStack.shift();
+ }
+
+ /**
+ * Marks the current function as having a lexical keyword. This implies that all arrow functions
+ * in the current lexical scope contain a reference to this lexical keyword.
+ * @returns {void}
+ */
+ function reportLexicalIdentifier() {
+ lexicalScopeStack[0].forEach(arrowFunction => arrowsWithLexicalIdentifiers.add(arrowFunction));
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: enterFunction,
+ FunctionDeclaration: enterFunction,
+ FunctionExpression: enterFunction,
+ "Program:exit": exitFunction,
+ "FunctionDeclaration:exit": exitFunction,
+ "FunctionExpression:exit": exitFunction,
+
+ ArrowFunctionExpression(node) {
+ lexicalScopeStack[0].add(node);
+ },
+ "ArrowFunctionExpression:exit"(node) {
+ lexicalScopeStack[0].delete(node);
+ },
+
+ ThisExpression: reportLexicalIdentifier,
+ Super: reportLexicalIdentifier,
+ MetaProperty(node) {
+ if (node.meta.name === "new" && node.property.name === "target") {
+ reportLexicalIdentifier();
+ }
+ },
+ Identifier(node) {
+ if (argumentsIdentifiers.has(node)) {
+ reportLexicalIdentifier();
+ }
+ },
+
+ ObjectExpression(node) {
+ if (APPLY_CONSISTENT) {
+ checkConsistency(node, false);
+ } else if (APPLY_CONSISTENT_AS_NEEDED) {
+ checkConsistency(node, true);
+ }
+ },
+
+ "Property:exit"(node) {
+ const isConciseProperty = node.method || node.shorthand;
+
+ // Ignore destructuring assignment
+ if (node.parent.type === "ObjectPattern") {
+ return;
+ }
+
+ // getters and setters are ignored
+ if (node.kind === "get" || node.kind === "set") {
+ return;
+ }
+
+ // only computed methods can fail the following checks
+ if (node.computed && node.value.type !== "FunctionExpression" && node.value.type !== "ArrowFunctionExpression") {
+ return;
+ }
+
+ //--------------------------------------------------------------
+ // Checks for property/method shorthand.
+ if (isConciseProperty) {
+ if (node.method && (APPLY_NEVER || AVOID_QUOTES && isStringLiteral(node.key))) {
+ const message = APPLY_NEVER ? "Expected longform method syntax." : "Expected longform method syntax for string literal keys.";
+
+ // { x() {} } should be written as { x: function() {} }
+ context.report({
+ node,
+ message,
+ fix: fixer => makeFunctionLongform(fixer, node)
+ });
+ } else if (APPLY_NEVER) {
+
+ // { x } should be written as { x: x }
+ context.report({
+ node,
+ message: "Expected longform property syntax.",
+ fix: fixer => fixer.insertTextAfter(node.key, `: ${node.key.name}`)
+ });
+ }
+ } else if (APPLY_TO_METHODS && !node.value.id && (node.value.type === "FunctionExpression" || node.value.type === "ArrowFunctionExpression")) {
+ if (IGNORE_CONSTRUCTORS && node.key.type === "Identifier" && isConstructor(node.key.name)) {
+ return;
+ }
+ if (AVOID_QUOTES && isStringLiteral(node.key)) {
+ return;
+ }
+
+ // {[x]: function(){}} should be written as {[x]() {}}
+ if (node.value.type === "FunctionExpression" ||
+ node.value.type === "ArrowFunctionExpression" &&
+ node.value.body.type === "BlockStatement" &&
+ AVOID_EXPLICIT_RETURN_ARROWS &&
+ !arrowsWithLexicalIdentifiers.has(node.value)
+ ) {
+ context.report({
+ node,
+ message: "Expected method shorthand.",
+ fix: fixer => makeFunctionShorthand(fixer, node)
+ });
+ }
+ } else if (node.value.type === "Identifier" && node.key.name === node.value.name && APPLY_TO_PROPS) {
+
+ // {x: x} should be written as {x}
+ context.report({
+ node,
+ message: "Expected property shorthand.",
+ fix(fixer) {
+ return fixer.replaceText(node, node.value.name);
+ }
+ });
+ } else if (node.value.type === "Identifier" && node.key.type === "Literal" && node.key.value === node.value.name && APPLY_TO_PROPS) {
+ if (AVOID_QUOTES) {
+ return;
+ }
+
+ // {"x": x} should be written as {x}
+ context.report({
+ node,
+ message: "Expected property shorthand.",
+ fix(fixer) {
+ return fixer.replaceText(node, node.value.name);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/one-var-declaration-per-line.js b/tools/node_modules/eslint/lib/rules/one-var-declaration-per-line.js
new file mode 100644
index 0000000000..61b505c82d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/one-var-declaration-per-line.js
@@ -0,0 +1,86 @@
+/**
+ * @fileoverview Rule to check multiple var declarations per line
+ * @author Alberto Rodríguez
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow newlines around variable declarations",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["always", "initializations"]
+ }
+ ],
+
+ fixable: "whitespace"
+ },
+
+ create(context) {
+
+ const ERROR_MESSAGE = "Expected variable declaration to be on a new line.";
+ const always = context.options[0] === "always";
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+
+ /**
+ * Determine if provided keyword is a variant of for specifiers
+ * @private
+ * @param {string} keyword - keyword to test
+ * @returns {boolean} True if `keyword` is a variant of for specifier
+ */
+ function isForTypeSpecifier(keyword) {
+ return keyword === "ForStatement" || keyword === "ForInStatement" || keyword === "ForOfStatement";
+ }
+
+ /**
+ * Checks newlines around variable declarations.
+ * @private
+ * @param {ASTNode} node - `VariableDeclaration` node to test
+ * @returns {void}
+ */
+ function checkForNewLine(node) {
+ if (isForTypeSpecifier(node.parent.type)) {
+ return;
+ }
+
+ const declarations = node.declarations;
+ let prev;
+
+ declarations.forEach(current => {
+ if (prev && prev.loc.end.line === current.loc.start.line) {
+ if (always || prev.init || current.init) {
+ context.report({
+ node,
+ message: ERROR_MESSAGE,
+ loc: current.loc.start,
+ fix: fixer => fixer.insertTextBefore(current, "\n")
+ });
+ }
+ }
+ prev = current;
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ VariableDeclaration: checkForNewLine
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/one-var.js b/tools/node_modules/eslint/lib/rules/one-var.js
new file mode 100644
index 0000000000..9e40d4ea6f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/one-var.js
@@ -0,0 +1,367 @@
+/**
+ * @fileoverview A rule to control the use of single variable declarations.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce variables to be declared either together or separately in functions",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ var: {
+ enum: ["always", "never"]
+ },
+ let: {
+ enum: ["always", "never"]
+ },
+ const: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false
+ },
+ {
+ type: "object",
+ properties: {
+ initialized: {
+ enum: ["always", "never"]
+ },
+ uninitialized: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const MODE_ALWAYS = "always",
+ MODE_NEVER = "never";
+
+ const mode = context.options[0] || MODE_ALWAYS;
+
+ const options = {
+ };
+
+ if (typeof mode === "string") { // simple options configuration with just a string
+ options.var = { uninitialized: mode, initialized: mode };
+ options.let = { uninitialized: mode, initialized: mode };
+ options.const = { uninitialized: mode, initialized: mode };
+ } else if (typeof mode === "object") { // options configuration is an object
+ if (mode.hasOwnProperty("var") && typeof mode.var === "string") {
+ options.var = { uninitialized: mode.var, initialized: mode.var };
+ }
+ if (mode.hasOwnProperty("let") && typeof mode.let === "string") {
+ options.let = { uninitialized: mode.let, initialized: mode.let };
+ }
+ if (mode.hasOwnProperty("const") && typeof mode.const === "string") {
+ options.const = { uninitialized: mode.const, initialized: mode.const };
+ }
+ if (mode.hasOwnProperty("uninitialized")) {
+ if (!options.var) {
+ options.var = {};
+ }
+ if (!options.let) {
+ options.let = {};
+ }
+ if (!options.const) {
+ options.const = {};
+ }
+ options.var.uninitialized = mode.uninitialized;
+ options.let.uninitialized = mode.uninitialized;
+ options.const.uninitialized = mode.uninitialized;
+ }
+ if (mode.hasOwnProperty("initialized")) {
+ if (!options.var) {
+ options.var = {};
+ }
+ if (!options.let) {
+ options.let = {};
+ }
+ if (!options.const) {
+ options.const = {};
+ }
+ options.var.initialized = mode.initialized;
+ options.let.initialized = mode.initialized;
+ options.const.initialized = mode.initialized;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ const functionStack = [];
+ const blockStack = [];
+
+ /**
+ * Increments the blockStack counter.
+ * @returns {void}
+ * @private
+ */
+ function startBlock() {
+ blockStack.push({
+ let: { initialized: false, uninitialized: false },
+ const: { initialized: false, uninitialized: false }
+ });
+ }
+
+ /**
+ * Increments the functionStack counter.
+ * @returns {void}
+ * @private
+ */
+ function startFunction() {
+ functionStack.push({ initialized: false, uninitialized: false });
+ startBlock();
+ }
+
+ /**
+ * Decrements the blockStack counter.
+ * @returns {void}
+ * @private
+ */
+ function endBlock() {
+ blockStack.pop();
+ }
+
+ /**
+ * Decrements the functionStack counter.
+ * @returns {void}
+ * @private
+ */
+ function endFunction() {
+ functionStack.pop();
+ endBlock();
+ }
+
+ /**
+ * Records whether initialized or uninitialized variables are defined in current scope.
+ * @param {string} statementType node.kind, one of: "var", "let", or "const"
+ * @param {ASTNode[]} declarations List of declarations
+ * @param {Object} currentScope The scope being investigated
+ * @returns {void}
+ * @private
+ */
+ function recordTypes(statementType, declarations, currentScope) {
+ for (let i = 0; i < declarations.length; i++) {
+ if (declarations[i].init === null) {
+ if (options[statementType] && options[statementType].uninitialized === MODE_ALWAYS) {
+ currentScope.uninitialized = true;
+ }
+ } else {
+ if (options[statementType] && options[statementType].initialized === MODE_ALWAYS) {
+ currentScope.initialized = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines the current scope (function or block)
+ * @param {string} statementType node.kind, one of: "var", "let", or "const"
+ * @returns {Object} The scope associated with statementType
+ */
+ function getCurrentScope(statementType) {
+ let currentScope;
+
+ if (statementType === "var") {
+ currentScope = functionStack[functionStack.length - 1];
+ } else if (statementType === "let") {
+ currentScope = blockStack[blockStack.length - 1].let;
+ } else if (statementType === "const") {
+ currentScope = blockStack[blockStack.length - 1].const;
+ }
+ return currentScope;
+ }
+
+ /**
+ * Counts the number of initialized and uninitialized declarations in a list of declarations
+ * @param {ASTNode[]} declarations List of declarations
+ * @returns {Object} Counts of 'uninitialized' and 'initialized' declarations
+ * @private
+ */
+ function countDeclarations(declarations) {
+ const counts = { uninitialized: 0, initialized: 0 };
+
+ for (let i = 0; i < declarations.length; i++) {
+ if (declarations[i].init === null) {
+ counts.uninitialized++;
+ } else {
+ counts.initialized++;
+ }
+ }
+ return counts;
+ }
+
+ /**
+ * Determines if there is more than one var statement in the current scope.
+ * @param {string} statementType node.kind, one of: "var", "let", or "const"
+ * @param {ASTNode[]} declarations List of declarations
+ * @returns {boolean} Returns true if it is the first var declaration, false if not.
+ * @private
+ */
+ function hasOnlyOneStatement(statementType, declarations) {
+
+ const declarationCounts = countDeclarations(declarations);
+ const currentOptions = options[statementType] || {};
+ const currentScope = getCurrentScope(statementType);
+
+ if (currentOptions.uninitialized === MODE_ALWAYS && currentOptions.initialized === MODE_ALWAYS) {
+ if (currentScope.uninitialized || currentScope.initialized) {
+ return false;
+ }
+ }
+
+ if (declarationCounts.uninitialized > 0) {
+ if (currentOptions.uninitialized === MODE_ALWAYS && currentScope.uninitialized) {
+ return false;
+ }
+ }
+ if (declarationCounts.initialized > 0) {
+ if (currentOptions.initialized === MODE_ALWAYS && currentScope.initialized) {
+ return false;
+ }
+ }
+ recordTypes(statementType, declarations, currentScope);
+ return true;
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: startFunction,
+ FunctionDeclaration: startFunction,
+ FunctionExpression: startFunction,
+ ArrowFunctionExpression: startFunction,
+ BlockStatement: startBlock,
+ ForStatement: startBlock,
+ ForInStatement: startBlock,
+ ForOfStatement: startBlock,
+ SwitchStatement: startBlock,
+
+ VariableDeclaration(node) {
+ const parent = node.parent;
+ const type = node.kind;
+
+ if (!options[type]) {
+ return;
+ }
+
+ const declarations = node.declarations;
+ const declarationCounts = countDeclarations(declarations);
+
+ // always
+ if (!hasOnlyOneStatement(type, declarations)) {
+ if (options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) {
+ context.report({
+ node,
+ message: "Combine this with the previous '{{type}}' statement.",
+ data: {
+ type
+ }
+ });
+ } else {
+ if (options[type].initialized === MODE_ALWAYS) {
+ context.report({
+ node,
+ message: "Combine this with the previous '{{type}}' statement with initialized variables.",
+ data: {
+ type
+ }
+ });
+ }
+ if (options[type].uninitialized === MODE_ALWAYS) {
+ if (node.parent.left === node && (node.parent.type === "ForInStatement" || node.parent.type === "ForOfStatement")) {
+ return;
+ }
+ context.report({
+ node,
+ message: "Combine this with the previous '{{type}}' statement with uninitialized variables.",
+ data: {
+ type
+ }
+ });
+ }
+ }
+ }
+
+ // never
+ if (parent.type !== "ForStatement" || parent.init !== node) {
+ const totalDeclarations = declarationCounts.uninitialized + declarationCounts.initialized;
+
+ if (totalDeclarations > 1) {
+
+ if (options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) {
+
+ // both initialized and uninitialized
+ context.report({
+ node,
+ message: "Split '{{type}}' declarations into multiple statements.",
+ data: {
+ type
+ }
+ });
+ } else if (options[type].initialized === MODE_NEVER && declarationCounts.initialized > 0) {
+
+ // initialized
+ context.report({
+ node,
+ message: "Split initialized '{{type}}' declarations into multiple statements.",
+ data: {
+ type
+ }
+ });
+ } else if (options[type].uninitialized === MODE_NEVER && declarationCounts.uninitialized > 0) {
+
+ // uninitialized
+ context.report({
+ node,
+ message: "Split uninitialized '{{type}}' declarations into multiple statements.",
+ data: {
+ type
+ }
+ });
+ }
+ }
+ }
+ },
+
+ "ForStatement:exit": endBlock,
+ "ForOfStatement:exit": endBlock,
+ "ForInStatement:exit": endBlock,
+ "SwitchStatement:exit": endBlock,
+ "BlockStatement:exit": endBlock,
+ "Program:exit": endFunction,
+ "FunctionDeclaration:exit": endFunction,
+ "FunctionExpression:exit": endFunction,
+ "ArrowFunctionExpression:exit": endFunction
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/operator-assignment.js b/tools/node_modules/eslint/lib/rules/operator-assignment.js
new file mode 100644
index 0000000000..f776609f5e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/operator-assignment.js
@@ -0,0 +1,206 @@
+/**
+ * @fileoverview Rule to replace assignment expressions with operator assignment
+ * @author Brandon Mills
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether an operator is commutative and has an operator assignment
+ * shorthand form.
+ * @param {string} operator Operator to check.
+ * @returns {boolean} True if the operator is commutative and has a
+ * shorthand form.
+ */
+function isCommutativeOperatorWithShorthand(operator) {
+ return ["*", "&", "^", "|"].indexOf(operator) >= 0;
+}
+
+/**
+ * Checks whether an operator is not commuatative and has an operator assignment
+ * shorthand form.
+ * @param {string} operator Operator to check.
+ * @returns {boolean} True if the operator is not commuatative and has
+ * a shorthand form.
+ */
+function isNonCommutativeOperatorWithShorthand(operator) {
+ return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].indexOf(operator) >= 0;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether two expressions reference the same value. For example:
+ * a = a
+ * a.b = a.b
+ * a[0] = a[0]
+ * a['b'] = a['b']
+ * @param {ASTNode} a Left side of the comparison.
+ * @param {ASTNode} b Right side of the comparison.
+ * @returns {boolean} True if both sides match and reference the same value.
+ */
+function same(a, b) {
+ if (a.type !== b.type) {
+ return false;
+ }
+
+ switch (a.type) {
+ case "Identifier":
+ return a.name === b.name;
+
+ case "Literal":
+ return a.value === b.value;
+
+ case "MemberExpression":
+
+ /*
+ * x[0] = x[0]
+ * x[y] = x[y]
+ * x.y = x.y
+ */
+ return same(a.object, b.object) && same(a.property, b.property);
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Determines if the left side of a node can be safely fixed (i.e. if it activates the same getters/setters and)
+ * toString calls regardless of whether assignment shorthand is used)
+ * @param {ASTNode} node The node on the left side of the expression
+ * @returns {boolean} `true` if the node can be fixed
+ */
+function canBeFixed(node) {
+ return node.type === "Identifier" ||
+ node.type === "MemberExpression" && node.object.type === "Identifier" && (!node.computed || node.property.type === "Literal");
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow assignment operator shorthand where possible",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Returns the operator token of an AssignmentExpression or BinaryExpression
+ * @param {ASTNode} node An AssignmentExpression or BinaryExpression node
+ * @returns {Token} The operator token in the node
+ */
+ function getOperatorToken(node) {
+ return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
+ }
+
+ /**
+ * Ensures that an assignment uses the shorthand form where possible.
+ * @param {ASTNode} node An AssignmentExpression node.
+ * @returns {void}
+ */
+ function verify(node) {
+ if (node.operator !== "=" || node.right.type !== "BinaryExpression") {
+ return;
+ }
+
+ const left = node.left;
+ const expr = node.right;
+ const operator = expr.operator;
+
+ if (isCommutativeOperatorWithShorthand(operator) || isNonCommutativeOperatorWithShorthand(operator)) {
+ if (same(left, expr.left)) {
+ context.report({
+ node,
+ message: "Assignment can be replaced with operator assignment.",
+ fix(fixer) {
+ if (canBeFixed(left)) {
+ const equalsToken = getOperatorToken(node);
+ const operatorToken = getOperatorToken(expr);
+ const leftText = sourceCode.getText().slice(node.range[0], equalsToken.range[0]);
+ const rightText = sourceCode.getText().slice(operatorToken.range[1], node.right.range[1]);
+
+ return fixer.replaceText(node, `${leftText}${expr.operator}=${rightText}`);
+ }
+ return null;
+ }
+ });
+ } else if (same(left, expr.right) && isCommutativeOperatorWithShorthand(operator)) {
+
+ /*
+ * This case can't be fixed safely.
+ * If `a` and `b` both have custom valueOf() behavior, then fixing `a = b * a` to `a *= b` would
+ * change the execution order of the valueOf() functions.
+ */
+ context.report({
+ node,
+ message: "Assignment can be replaced with operator assignment."
+ });
+ }
+ }
+ }
+
+ /**
+ * Warns if an assignment expression uses operator assignment shorthand.
+ * @param {ASTNode} node An AssignmentExpression node.
+ * @returns {void}
+ */
+ function prohibit(node) {
+ if (node.operator !== "=") {
+ context.report({
+ node,
+ message: "Unexpected operator assignment shorthand.",
+ fix(fixer) {
+ if (canBeFixed(node.left)) {
+ const operatorToken = getOperatorToken(node);
+ const leftText = sourceCode.getText().slice(node.range[0], operatorToken.range[0]);
+ const newOperator = node.operator.slice(0, -1);
+ let rightText;
+
+ // If this change would modify precedence (e.g. `foo *= bar + 1` => `foo = foo * (bar + 1)`), parenthesize the right side.
+ if (
+ astUtils.getPrecedence(node.right) <= astUtils.getPrecedence({ type: "BinaryExpression", operator: newOperator }) &&
+ !astUtils.isParenthesised(sourceCode, node.right)
+ ) {
+ rightText = `${sourceCode.text.slice(operatorToken.range[1], node.right.range[0])}(${sourceCode.getText(node.right)})`;
+ } else {
+ rightText = sourceCode.text.slice(operatorToken.range[1], node.range[1]);
+ }
+
+ return fixer.replaceText(node, `${leftText}= ${leftText}${newOperator}${rightText}`);
+ }
+ return null;
+ }
+ });
+ }
+ }
+
+ return {
+ AssignmentExpression: context.options[0] !== "never" ? verify : prohibit
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/operator-linebreak.js b/tools/node_modules/eslint/lib/rules/operator-linebreak.js
new file mode 100644
index 0000000000..271cbb35c1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/operator-linebreak.js
@@ -0,0 +1,252 @@
+/**
+ * @fileoverview Operator linebreak - enforces operator linebreak style of two types: after and before
+ * @author Benoît Zugmeyer
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent linebreak style for operators",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["after", "before", "none", null]
+ },
+ {
+ type: "object",
+ properties: {
+ overrides: {
+ type: "object",
+ properties: {
+ anyOf: {
+ type: "string",
+ enum: ["after", "before", "none", "ignore"]
+ }
+ }
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const usedDefaultGlobal = !context.options[0];
+ const globalStyle = context.options[0] || "after";
+ const options = context.options[1] || {};
+ const styleOverrides = options.overrides ? Object.assign({}, options.overrides) : {};
+
+ if (usedDefaultGlobal && !styleOverrides["?"]) {
+ styleOverrides["?"] = "before";
+ }
+
+ if (usedDefaultGlobal && !styleOverrides[":"]) {
+ styleOverrides[":"] = "before";
+ }
+
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Gets a fixer function to fix rule issues
+ * @param {Token} operatorToken The operator token of an expression
+ * @param {string} desiredStyle The style for the rule. One of 'before', 'after', 'none'
+ * @returns {Function} A fixer function
+ */
+ function getFixer(operatorToken, desiredStyle) {
+ return fixer => {
+ const tokenBefore = sourceCode.getTokenBefore(operatorToken);
+ const tokenAfter = sourceCode.getTokenAfter(operatorToken);
+ const textBefore = sourceCode.text.slice(tokenBefore.range[1], operatorToken.range[0]);
+ const textAfter = sourceCode.text.slice(operatorToken.range[1], tokenAfter.range[0]);
+ const hasLinebreakBefore = !astUtils.isTokenOnSameLine(tokenBefore, operatorToken);
+ const hasLinebreakAfter = !astUtils.isTokenOnSameLine(operatorToken, tokenAfter);
+ let newTextBefore, newTextAfter;
+
+ if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
+
+ // If there is a comment before and after the operator, don't do a fix.
+ if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore &&
+ sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
+
+ return null;
+ }
+
+ /*
+ * If there is only one linebreak and it's on the wrong side of the operator, swap the text before and after the operator.
+ * foo &&
+ * bar
+ * would get fixed to
+ * foo
+ * && bar
+ */
+ newTextBefore = textAfter;
+ newTextAfter = textBefore;
+ } else {
+ const LINEBREAK_REGEX = astUtils.createGlobalLinebreakMatcher();
+
+ // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
+ newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
+ newTextAfter = desiredStyle === "after" || textAfter.trim() ? textAfter : textAfter.replace(LINEBREAK_REGEX, "");
+
+ // If there was no change (due to interfering comments), don't output a fix.
+ if (newTextBefore === textBefore && newTextAfter === textAfter) {
+ return null;
+ }
+ }
+
+ if (newTextAfter === "" && tokenAfter.type === "Punctuator" && "+-".includes(operatorToken.value) && tokenAfter.value === operatorToken.value) {
+
+ // To avoid accidentally creating a ++ or -- operator, insert a space if the operator is a +/- and the following token is a unary +/-.
+ newTextAfter += " ";
+ }
+
+ return fixer.replaceTextRange([tokenBefore.range[1], tokenAfter.range[0]], newTextBefore + operatorToken.value + newTextAfter);
+ };
+ }
+
+ /**
+ * Checks the operator placement
+ * @param {ASTNode} node The node to check
+ * @param {ASTNode} leftSide The node that comes before the operator in `node`
+ * @private
+ * @returns {void}
+ */
+ function validateNode(node, leftSide) {
+
+ /*
+ * When the left part of a binary expression is a single expression wrapped in
+ * parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
+ * and operatorToken will be the closing parenthesis.
+ * The leftToken should be the last closing parenthesis, and the operatorToken
+ * should be the token right after that.
+ */
+ const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
+ const leftToken = sourceCode.getTokenBefore(operatorToken);
+ const rightToken = sourceCode.getTokenAfter(operatorToken);
+ const operator = operatorToken.value;
+ const operatorStyleOverride = styleOverrides[operator];
+ const style = operatorStyleOverride || globalStyle;
+ const fix = getFixer(operatorToken, style);
+
+ // if single line
+ if (astUtils.isTokenOnSameLine(leftToken, operatorToken) &&
+ astUtils.isTokenOnSameLine(operatorToken, rightToken)) {
+
+ // do nothing.
+
+ } else if (operatorStyleOverride !== "ignore" && !astUtils.isTokenOnSameLine(leftToken, operatorToken) &&
+ !astUtils.isTokenOnSameLine(operatorToken, rightToken)) {
+
+ // lone operator
+ context.report({
+ node,
+ loc: {
+ line: operatorToken.loc.end.line,
+ column: operatorToken.loc.end.column
+ },
+ message: "Bad line breaking before and after '{{operator}}'.",
+ data: {
+ operator
+ },
+ fix
+ });
+
+ } else if (style === "before" && astUtils.isTokenOnSameLine(leftToken, operatorToken)) {
+
+ context.report({
+ node,
+ loc: {
+ line: operatorToken.loc.end.line,
+ column: operatorToken.loc.end.column
+ },
+ message: "'{{operator}}' should be placed at the beginning of the line.",
+ data: {
+ operator
+ },
+ fix
+ });
+
+ } else if (style === "after" && astUtils.isTokenOnSameLine(operatorToken, rightToken)) {
+
+ context.report({
+ node,
+ loc: {
+ line: operatorToken.loc.end.line,
+ column: operatorToken.loc.end.column
+ },
+ message: "'{{operator}}' should be placed at the end of the line.",
+ data: {
+ operator
+ },
+ fix
+ });
+
+ } else if (style === "none") {
+
+ context.report({
+ node,
+ loc: {
+ line: operatorToken.loc.end.line,
+ column: operatorToken.loc.end.column
+ },
+ message: "There should be no line break before or after '{{operator}}'.",
+ data: {
+ operator
+ },
+ fix
+ });
+
+ }
+ }
+
+ /**
+ * Validates a binary expression using `validateNode`
+ * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated
+ * @returns {void}
+ */
+ function validateBinaryExpression(node) {
+ validateNode(node, node.left);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ BinaryExpression: validateBinaryExpression,
+ LogicalExpression: validateBinaryExpression,
+ AssignmentExpression: validateBinaryExpression,
+ VariableDeclarator(node) {
+ if (node.init) {
+ validateNode(node, node.id);
+ }
+ },
+ ConditionalExpression(node) {
+ validateNode(node, node.test);
+ validateNode(node, node.consequent);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/padded-blocks.js b/tools/node_modules/eslint/lib/rules/padded-blocks.js
new file mode 100644
index 0000000000..ad65882ac6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/padded-blocks.js
@@ -0,0 +1,256 @@
+/**
+ * @fileoverview A rule to ensure blank lines within blocks.
+ * @author Mathias Schreck <https://github.com/lo1tuma>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow padding within blocks",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ blocks: {
+ enum: ["always", "never"]
+ },
+ switches: {
+ enum: ["always", "never"]
+ },
+ classes: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false,
+ minProperties: 1
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const options = {};
+ const config = context.options[0] || "always";
+
+ if (typeof config === "string") {
+ const shouldHavePadding = config === "always";
+
+ options.blocks = shouldHavePadding;
+ options.switches = shouldHavePadding;
+ options.classes = shouldHavePadding;
+ } else {
+ if (config.hasOwnProperty("blocks")) {
+ options.blocks = config.blocks === "always";
+ }
+ if (config.hasOwnProperty("switches")) {
+ options.switches = config.switches === "always";
+ }
+ if (config.hasOwnProperty("classes")) {
+ options.classes = config.classes === "always";
+ }
+ }
+
+ const ALWAYS_MESSAGE = "Block must be padded by blank lines.",
+ NEVER_MESSAGE = "Block must not be padded by blank lines.";
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Gets the open brace token from a given node.
+ * @param {ASTNode} node - A BlockStatement or SwitchStatement node from which to get the open brace.
+ * @returns {Token} The token of the open brace.
+ */
+ function getOpenBrace(node) {
+ if (node.type === "SwitchStatement") {
+ return sourceCode.getTokenBefore(node.cases[0]);
+ }
+ return sourceCode.getFirstToken(node);
+ }
+
+ /**
+ * Checks if the given parameter is a comment node
+ * @param {ASTNode|Token} node An AST node or token
+ * @returns {boolean} True if node is a comment
+ */
+ function isComment(node) {
+ return node.type === "Line" || node.type === "Block";
+ }
+
+ /**
+ * Checks if there is padding between two tokens
+ * @param {Token} first The first token
+ * @param {Token} second The second token
+ * @returns {boolean} True if there is at least a line between the tokens
+ */
+ function isPaddingBetweenTokens(first, second) {
+ return second.loc.start.line - first.loc.end.line >= 2;
+ }
+
+
+ /**
+ * Checks if the given token has a blank line after it.
+ * @param {Token} token The token to check.
+ * @returns {boolean} Whether or not the token is followed by a blank line.
+ */
+ function getFirstBlockToken(token) {
+ let prev,
+ first = token;
+
+ do {
+ prev = first;
+ first = sourceCode.getTokenAfter(first, { includeComments: true });
+ } while (isComment(first) && first.loc.start.line === prev.loc.end.line);
+
+ return first;
+ }
+
+ /**
+ * Checks if the given token is preceeded by a blank line.
+ * @param {Token} token The token to check
+ * @returns {boolean} Whether or not the token is preceeded by a blank line
+ */
+ function getLastBlockToken(token) {
+ let last = token,
+ next;
+
+ do {
+ next = last;
+ last = sourceCode.getTokenBefore(last, { includeComments: true });
+ } while (isComment(last) && last.loc.end.line === next.loc.start.line);
+
+ return last;
+ }
+
+ /**
+ * Checks if a node should be padded, according to the rule config.
+ * @param {ASTNode} node The AST node to check.
+ * @returns {boolean} True if the node should be padded, false otherwise.
+ */
+ function requirePaddingFor(node) {
+ switch (node.type) {
+ case "BlockStatement":
+ return options.blocks;
+ case "SwitchStatement":
+ return options.switches;
+ case "ClassBody":
+ return options.classes;
+
+ /* istanbul ignore next */
+ default:
+ throw new Error("unreachable");
+ }
+ }
+
+ /**
+ * Checks the given BlockStatement node to be padded if the block is not empty.
+ * @param {ASTNode} node The AST node of a BlockStatement.
+ * @returns {void} undefined.
+ */
+ function checkPadding(node) {
+ const openBrace = getOpenBrace(node),
+ firstBlockToken = getFirstBlockToken(openBrace),
+ tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }),
+ closeBrace = sourceCode.getLastToken(node),
+ lastBlockToken = getLastBlockToken(closeBrace),
+ tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }),
+ blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),
+ blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);
+
+ if (requirePaddingFor(node)) {
+ if (!blockHasTopPadding) {
+ context.report({
+ node,
+ loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
+ fix(fixer) {
+ return fixer.insertTextAfter(tokenBeforeFirst, "\n");
+ },
+ message: ALWAYS_MESSAGE
+ });
+ }
+ if (!blockHasBottomPadding) {
+ context.report({
+ node,
+ loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
+ fix(fixer) {
+ return fixer.insertTextBefore(tokenAfterLast, "\n");
+ },
+ message: ALWAYS_MESSAGE
+ });
+ }
+ } else {
+ if (blockHasTopPadding) {
+
+ context.report({
+ node,
+ loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
+ fix(fixer) {
+ return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n");
+ },
+ message: NEVER_MESSAGE
+ });
+ }
+
+ if (blockHasBottomPadding) {
+
+ context.report({
+ node,
+ loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
+ message: NEVER_MESSAGE,
+ fix(fixer) {
+ return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n");
+ }
+ });
+ }
+ }
+ }
+
+ const rule = {};
+
+ if (options.hasOwnProperty("switches")) {
+ rule.SwitchStatement = function(node) {
+ if (node.cases.length === 0) {
+ return;
+ }
+ checkPadding(node);
+ };
+ }
+
+ if (options.hasOwnProperty("blocks")) {
+ rule.BlockStatement = function(node) {
+ if (node.body.length === 0) {
+ return;
+ }
+ checkPadding(node);
+ };
+ }
+
+ if (options.hasOwnProperty("classes")) {
+ rule.ClassBody = function(node) {
+ if (node.body.length === 0) {
+ return;
+ }
+ checkPadding(node);
+ };
+ }
+
+ return rule;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/padding-line-between-statements.js b/tools/node_modules/eslint/lib/rules/padding-line-between-statements.js
new file mode 100644
index 0000000000..a89c49decf
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/padding-line-between-statements.js
@@ -0,0 +1,589 @@
+/**
+ * @fileoverview Rule to require or disallow newlines between statements
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const LT = `[${Array.from(astUtils.LINEBREAKS).join("")}]`;
+const PADDING_LINE_SEQUENCE = new RegExp(
+ String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`
+);
+const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/;
+const CJS_IMPORT = /^require\(/;
+
+/**
+ * Creates tester which check if a node starts with specific keyword.
+ *
+ * @param {string} keyword The keyword to test.
+ * @returns {Object} the created tester.
+ * @private
+ */
+function newKeywordTester(keyword) {
+ return {
+ test: (node, sourceCode) =>
+ sourceCode.getFirstToken(node).value === keyword
+ };
+}
+
+/**
+ * Creates tester which check if a node is specific type.
+ *
+ * @param {string} type The node type to test.
+ * @returns {Object} the created tester.
+ * @private
+ */
+function newNodeTypeTester(type) {
+ return {
+ test: node =>
+ node.type === type
+ };
+}
+
+/**
+ * Checks the given node is an expression statement of IIFE.
+ *
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is an expression statement of IIFE.
+ * @private
+ */
+function isIIFEStatement(node) {
+ if (node.type === "ExpressionStatement") {
+ let call = node.expression;
+
+ if (call.type === "UnaryExpression") {
+ call = call.argument;
+ }
+ return call.type === "CallExpression" && astUtils.isFunction(call.callee);
+ }
+ return false;
+}
+
+/**
+ * Checks whether the given node is a block-like statement.
+ * This checks the last token of the node is the closing brace of a block.
+ *
+ * @param {SourceCode} sourceCode The source code to get tokens.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a block-like statement.
+ * @private
+ */
+function isBlockLikeStatement(sourceCode, node) {
+
+ // do-while with a block is a block-like statement.
+ if (node.type === "DoWhileStatement" && node.body.type === "BlockStatement") {
+ return true;
+ }
+
+ /*
+ * IIFE is a block-like statement specially from
+ * JSCS#disallowPaddingNewLinesAfterBlocks.
+ */
+ if (isIIFEStatement(node)) {
+ return true;
+ }
+
+ // Checks the last token is a closing brace of blocks.
+ const lastToken = sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
+ const belongingNode = lastToken && astUtils.isClosingBraceToken(lastToken)
+ ? sourceCode.getNodeByRangeIndex(lastToken.range[0])
+ : null;
+
+ return Boolean(belongingNode) && (
+ belongingNode.type === "BlockStatement" ||
+ belongingNode.type === "SwitchStatement"
+ );
+}
+
+/**
+ * Check whether the given node is a directive or not.
+ * @param {ASTNode} node The node to check.
+ * @param {SourceCode} sourceCode The source code object to get tokens.
+ * @returns {boolean} `true` if the node is a directive.
+ */
+function isDirective(node, sourceCode) {
+ return (
+ node.type === "ExpressionStatement" &&
+ (
+ node.parent.type === "Program" ||
+ (
+ node.parent.type === "BlockStatement" &&
+ astUtils.isFunction(node.parent.parent)
+ )
+ ) &&
+ node.expression.type === "Literal" &&
+ typeof node.expression.value === "string" &&
+ !astUtils.isParenthesised(sourceCode, node.expression)
+ );
+}
+
+/**
+ * Check whether the given node is a part of directive prologue or not.
+ * @param {ASTNode} node The node to check.
+ * @param {SourceCode} sourceCode The source code object to get tokens.
+ * @returns {boolean} `true` if the node is a part of directive prologue.
+ */
+function isDirectivePrologue(node, sourceCode) {
+ if (isDirective(node, sourceCode)) {
+ for (const sibling of node.parent.body) {
+ if (sibling === node) {
+ break;
+ }
+ if (!isDirective(sibling, sourceCode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Gets the actual last token.
+ *
+ * If a semicolon is semicolon-less style's semicolon, this ignores it.
+ * For example:
+ *
+ * foo()
+ * ;[1, 2, 3].forEach(bar)
+ *
+ * @param {SourceCode} sourceCode The source code to get tokens.
+ * @param {ASTNode} node The node to get.
+ * @returns {Token} The actual last token.
+ * @private
+ */
+function getActualLastToken(sourceCode, node) {
+ const semiToken = sourceCode.getLastToken(node);
+ const prevToken = sourceCode.getTokenBefore(semiToken);
+ const nextToken = sourceCode.getTokenAfter(semiToken);
+ const isSemicolonLessStyle = Boolean(
+ prevToken &&
+ nextToken &&
+ prevToken.range[0] >= node.range[0] &&
+ astUtils.isSemicolonToken(semiToken) &&
+ semiToken.loc.start.line !== prevToken.loc.end.line &&
+ semiToken.loc.end.line === nextToken.loc.start.line
+ );
+
+ return isSemicolonLessStyle ? prevToken : semiToken;
+}
+
+/**
+ * This returns the concatenation of the first 2 captured strings.
+ * @param {string} _ Unused. Whole matched string.
+ * @param {string} trailingSpaces The trailing spaces of the first line.
+ * @param {string} indentSpaces The indentation spaces of the last line.
+ * @returns {string} The concatenation of trailingSpaces and indentSpaces.
+ * @private
+ */
+function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
+ return trailingSpaces + indentSpaces;
+}
+
+/**
+ * Check and report statements for `any` configuration.
+ * It does nothing.
+ *
+ * @returns {void}
+ * @private
+ */
+function verifyForAny() {
+}
+
+/**
+ * Check and report statements for `never` configuration.
+ * This autofix removes blank lines between the given 2 statements.
+ * However, if comments exist between 2 blank lines, it does not remove those
+ * blank lines automatically.
+ *
+ * @param {RuleContext} context The rule context to report.
+ * @param {ASTNode} _ Unused. The previous node to check.
+ * @param {ASTNode} nextNode The next node to check.
+ * @param {Array<Token[]>} paddingLines The array of token pairs that blank
+ * lines exist between the pair.
+ * @returns {void}
+ * @private
+ */
+function verifyForNever(context, _, nextNode, paddingLines) {
+ if (paddingLines.length === 0) {
+ return;
+ }
+
+ context.report({
+ node: nextNode,
+ message: "Unexpected blank line before this statement.",
+ fix(fixer) {
+ if (paddingLines.length >= 2) {
+ return null;
+ }
+
+ const prevToken = paddingLines[0][0];
+ const nextToken = paddingLines[0][1];
+ const start = prevToken.range[1];
+ const end = nextToken.range[0];
+ const text = context.getSourceCode().text
+ .slice(start, end)
+ .replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
+
+ return fixer.replaceTextRange([start, end], text);
+ }
+ });
+}
+
+/**
+ * Check and report statements for `always` configuration.
+ * This autofix inserts a blank line between the given 2 statements.
+ * If the `prevNode` has trailing comments, it inserts a blank line after the
+ * trailing comments.
+ *
+ * @param {RuleContext} context The rule context to report.
+ * @param {ASTNode} prevNode The previous node to check.
+ * @param {ASTNode} nextNode The next node to check.
+ * @param {Array<Token[]>} paddingLines The array of token pairs that blank
+ * lines exist between the pair.
+ * @returns {void}
+ * @private
+ */
+function verifyForAlways(context, prevNode, nextNode, paddingLines) {
+ if (paddingLines.length > 0) {
+ return;
+ }
+
+ context.report({
+ node: nextNode,
+ message: "Expected blank line before this statement.",
+ fix(fixer) {
+ const sourceCode = context.getSourceCode();
+ let prevToken = getActualLastToken(sourceCode, prevNode);
+ const nextToken = sourceCode.getFirstTokenBetween(
+ prevToken,
+ nextNode,
+ {
+ includeComments: true,
+
+ /**
+ * Skip the trailing comments of the previous node.
+ * This inserts a blank line after the last trailing comment.
+ *
+ * For example:
+ *
+ * foo(); // trailing comment.
+ * // comment.
+ * bar();
+ *
+ * Get fixed to:
+ *
+ * foo(); // trailing comment.
+ *
+ * // comment.
+ * bar();
+ *
+ * @param {Token} token The token to check.
+ * @returns {boolean} `true` if the token is not a trailing comment.
+ * @private
+ */
+ filter(token) {
+ if (astUtils.isTokenOnSameLine(prevToken, token)) {
+ prevToken = token;
+ return false;
+ }
+ return true;
+ }
+ }
+ ) || nextNode;
+ const insertText = astUtils.isTokenOnSameLine(prevToken, nextToken)
+ ? "\n\n"
+ : "\n";
+
+ return fixer.insertTextAfter(prevToken, insertText);
+ }
+ });
+}
+
+/**
+ * Types of blank lines.
+ * `any`, `never`, and `always` are defined.
+ * Those have `verify` method to check and report statements.
+ * @private
+ */
+const PaddingTypes = {
+ any: { verify: verifyForAny },
+ never: { verify: verifyForNever },
+ always: { verify: verifyForAlways }
+};
+
+/**
+ * Types of statements.
+ * Those have `test` method to check it matches to the given statement.
+ * @private
+ */
+const StatementTypes = {
+ "*": { test: () => true },
+ "block-like": {
+ test: (node, sourceCode) => isBlockLikeStatement(sourceCode, node)
+ },
+ "cjs-export": {
+ test: (node, sourceCode) =>
+ node.type === "ExpressionStatement" &&
+ node.expression.type === "AssignmentExpression" &&
+ CJS_EXPORT.test(sourceCode.getText(node.expression.left))
+ },
+ "cjs-import": {
+ test: (node, sourceCode) =>
+ node.type === "VariableDeclaration" &&
+ node.declarations.length > 0 &&
+ Boolean(node.declarations[0].init) &&
+ CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
+ },
+ directive: {
+ test: isDirectivePrologue
+ },
+ expression: {
+ test: (node, sourceCode) =>
+ node.type === "ExpressionStatement" &&
+ !isDirectivePrologue(node, sourceCode)
+ },
+ "multiline-block-like": {
+ test: (node, sourceCode) =>
+ node.loc.start.line !== node.loc.end.line &&
+ isBlockLikeStatement(sourceCode, node)
+ },
+
+ block: newNodeTypeTester("BlockStatement"),
+ empty: newNodeTypeTester("EmptyStatement"),
+
+ break: newKeywordTester("break"),
+ case: newKeywordTester("case"),
+ class: newKeywordTester("class"),
+ const: newKeywordTester("const"),
+ continue: newKeywordTester("continue"),
+ debugger: newKeywordTester("debugger"),
+ default: newKeywordTester("default"),
+ do: newKeywordTester("do"),
+ export: newKeywordTester("export"),
+ for: newKeywordTester("for"),
+ function: newKeywordTester("function"),
+ if: newKeywordTester("if"),
+ import: newKeywordTester("import"),
+ let: newKeywordTester("let"),
+ return: newKeywordTester("return"),
+ switch: newKeywordTester("switch"),
+ throw: newKeywordTester("throw"),
+ try: newKeywordTester("try"),
+ var: newKeywordTester("var"),
+ while: newKeywordTester("while"),
+ with: newKeywordTester("with")
+};
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow padding lines between statements",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: {
+ definitions: {
+ paddingType: {
+ enum: Object.keys(PaddingTypes)
+ },
+ statementType: {
+ anyOf: [
+ { enum: Object.keys(StatementTypes) },
+ {
+ type: "array",
+ items: { enum: Object.keys(StatementTypes) },
+ minItems: 1,
+ uniqueItems: true,
+ additionalItems: false
+ }
+ ]
+ }
+ },
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ blankLine: { $ref: "#/definitions/paddingType" },
+ prev: { $ref: "#/definitions/statementType" },
+ next: { $ref: "#/definitions/statementType" }
+ },
+ additionalProperties: false,
+ required: ["blankLine", "prev", "next"]
+ },
+ additionalItems: false
+ }
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const configureList = context.options || [];
+ let scopeInfo = null;
+
+ /**
+ * Processes to enter to new scope.
+ * This manages the current previous statement.
+ * @returns {void}
+ * @private
+ */
+ function enterScope() {
+ scopeInfo = {
+ upper: scopeInfo,
+ prevNode: null
+ };
+ }
+
+ /**
+ * Processes to exit from the current scope.
+ * @returns {void}
+ * @private
+ */
+ function exitScope() {
+ scopeInfo = scopeInfo.upper;
+ }
+
+ /**
+ * Checks whether the given node matches the given type.
+ *
+ * @param {ASTNode} node The statement node to check.
+ * @param {string|string[]} type The statement type to check.
+ * @returns {boolean} `true` if the statement node matched the type.
+ * @private
+ */
+ function match(node, type) {
+ while (node.type === "LabeledStatement") {
+ node = node.body;
+ }
+ if (Array.isArray(type)) {
+ return type.some(match.bind(null, node));
+ }
+ return StatementTypes[type].test(node, sourceCode);
+ }
+
+ /**
+ * Finds the last matched configure from configureList.
+ *
+ * @param {ASTNode} prevNode The previous statement to match.
+ * @param {ASTNode} nextNode The current statement to match.
+ * @returns {Object} The tester of the last matched configure.
+ * @private
+ */
+ function getPaddingType(prevNode, nextNode) {
+ for (let i = configureList.length - 1; i >= 0; --i) {
+ const configure = configureList[i];
+ const matched =
+ match(prevNode, configure.prev) &&
+ match(nextNode, configure.next);
+
+ if (matched) {
+ return PaddingTypes[configure.blankLine];
+ }
+ }
+ return PaddingTypes.any;
+ }
+
+ /**
+ * Gets padding line sequences between the given 2 statements.
+ * Comments are separators of the padding line sequences.
+ *
+ * @param {ASTNode} prevNode The previous statement to count.
+ * @param {ASTNode} nextNode The current statement to count.
+ * @returns {Array<Token[]>} The array of token pairs.
+ * @private
+ */
+ function getPaddingLineSequences(prevNode, nextNode) {
+ const pairs = [];
+ let prevToken = getActualLastToken(sourceCode, prevNode);
+
+ if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
+ do {
+ const token = sourceCode.getTokenAfter(
+ prevToken,
+ { includeComments: true }
+ );
+
+ if (token.loc.start.line - prevToken.loc.end.line >= 2) {
+ pairs.push([prevToken, token]);
+ }
+ prevToken = token;
+
+ } while (prevToken.range[0] < nextNode.range[0]);
+ }
+
+ return pairs;
+ }
+
+ /**
+ * Verify padding lines between the given node and the previous node.
+ *
+ * @param {ASTNode} node The node to verify.
+ * @returns {void}
+ * @private
+ */
+ function verify(node) {
+ const parentType = node.parent.type;
+ const validParent =
+ astUtils.STATEMENT_LIST_PARENTS.has(parentType) ||
+ parentType === "SwitchStatement";
+
+ if (!validParent) {
+ return;
+ }
+
+ // Save this node as the current previous statement.
+ const prevNode = scopeInfo.prevNode;
+
+ // Verify.
+ if (prevNode) {
+ const type = getPaddingType(prevNode, node);
+ const paddingLines = getPaddingLineSequences(prevNode, node);
+
+ type.verify(context, prevNode, node, paddingLines);
+ }
+
+ scopeInfo.prevNode = node;
+ }
+
+ /**
+ * Verify padding lines between the given node and the previous node.
+ * Then process to enter to new scope.
+ *
+ * @param {ASTNode} node The node to verify.
+ * @returns {void}
+ * @private
+ */
+ function verifyThenEnterScope(node) {
+ verify(node);
+ enterScope();
+ }
+
+ return {
+ Program: enterScope,
+ BlockStatement: enterScope,
+ SwitchStatement: enterScope,
+ "Program:exit": exitScope,
+ "BlockStatement:exit": exitScope,
+ "SwitchStatement:exit": exitScope,
+
+ ":statement": verify,
+
+ SwitchCase: verifyThenEnterScope,
+ "SwitchCase:exit": exitScope
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-arrow-callback.js b/tools/node_modules/eslint/lib/rules/prefer-arrow-callback.js
new file mode 100644
index 0000000000..31ae2859fe
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-arrow-callback.js
@@ -0,0 +1,304 @@
+/**
+ * @fileoverview A rule to suggest using arrow functions as callbacks.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given variable is a function name.
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @returns {boolean} `true` if the variable is a function name.
+ */
+function isFunctionName(variable) {
+ return variable && variable.defs[0].type === "FunctionName";
+}
+
+/**
+ * Checks whether or not a given MetaProperty node equals to a given value.
+ * @param {ASTNode} node - A MetaProperty node to check.
+ * @param {string} metaName - The name of `MetaProperty.meta`.
+ * @param {string} propertyName - The name of `MetaProperty.property`.
+ * @returns {boolean} `true` if the node is the specific value.
+ */
+function checkMetaProperty(node, metaName, propertyName) {
+ return node.meta.name === metaName && node.property.name === propertyName;
+}
+
+/**
+ * Gets the variable object of `arguments` which is defined implicitly.
+ * @param {eslint-scope.Scope} scope - A scope to get.
+ * @returns {eslint-scope.Variable} The found variable object.
+ */
+function getVariableOfArguments(scope) {
+ const variables = scope.variables;
+
+ for (let i = 0; i < variables.length; ++i) {
+ const variable = variables[i];
+
+ if (variable.name === "arguments") {
+
+ /*
+ * If there was a parameter which is named "arguments", the
+ * implicit "arguments" is not defined.
+ * So does fast return with null.
+ */
+ return (variable.identifiers.length === 0) ? variable : null;
+ }
+ }
+
+ /* istanbul ignore next */
+ return null;
+}
+
+/**
+ * Checkes whether or not a given node is a callback.
+ * @param {ASTNode} node - A node to check.
+ * @returns {Object}
+ * {boolean} retv.isCallback - `true` if the node is a callback.
+ * {boolean} retv.isLexicalThis - `true` if the node is with `.bind(this)`.
+ */
+function getCallbackInfo(node) {
+ const retv = { isCallback: false, isLexicalThis: false };
+ let parent = node.parent;
+
+ while (node) {
+ switch (parent.type) {
+
+ // Checks parents recursively.
+
+ case "LogicalExpression":
+ case "ConditionalExpression":
+ break;
+
+ // Checks whether the parent node is `.bind(this)` call.
+ case "MemberExpression":
+ if (parent.object === node &&
+ !parent.property.computed &&
+ parent.property.type === "Identifier" &&
+ parent.property.name === "bind" &&
+ parent.parent.type === "CallExpression" &&
+ parent.parent.callee === parent
+ ) {
+ retv.isLexicalThis = (
+ parent.parent.arguments.length === 1 &&
+ parent.parent.arguments[0].type === "ThisExpression"
+ );
+ parent = parent.parent;
+ } else {
+ return retv;
+ }
+ break;
+
+ // Checks whether the node is a callback.
+ case "CallExpression":
+ case "NewExpression":
+ if (parent.callee !== node) {
+ retv.isCallback = true;
+ }
+ return retv;
+
+ default:
+ return retv;
+ }
+
+ node = parent;
+ parent = parent.parent;
+ }
+
+ /* istanbul ignore next */
+ throw new Error("unreachable");
+}
+
+/**
+ * Checks whether a simple list of parameters contains any duplicates. This does not handle complex
+ * parameter lists (e.g. with destructuring), since complex parameter lists are a SyntaxError with duplicate
+ * parameter names anyway. Instead, it always returns `false` for complex parameter lists.
+ * @param {ASTNode[]} paramsList The list of parameters for a function
+ * @returns {boolean} `true` if the list of parameters contains any duplicates
+ */
+function hasDuplicateParams(paramsList) {
+ return paramsList.every(param => param.type === "Identifier") && paramsList.length !== new Set(paramsList.map(param => param.name)).size;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require using arrow functions for callbacks",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowNamedFunctions: {
+ type: "boolean"
+ },
+ allowUnboundThis: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+
+ const allowUnboundThis = options.allowUnboundThis !== false; // default to true
+ const allowNamedFunctions = options.allowNamedFunctions;
+ const sourceCode = context.getSourceCode();
+
+ /*
+ * {Array<{this: boolean, super: boolean, meta: boolean}>}
+ * - this - A flag which shows there are one or more ThisExpression.
+ * - super - A flag which shows there are one or more Super.
+ * - meta - A flag which shows there are one or more MethProperty.
+ */
+ let stack = [];
+
+ /**
+ * Pushes new function scope with all `false` flags.
+ * @returns {void}
+ */
+ function enterScope() {
+ stack.push({ this: false, super: false, meta: false });
+ }
+
+ /**
+ * Pops a function scope from the stack.
+ * @returns {{this: boolean, super: boolean, meta: boolean}} The information of the last scope.
+ */
+ function exitScope() {
+ return stack.pop();
+ }
+
+ return {
+
+ // Reset internal state.
+ Program() {
+ stack = [];
+ },
+
+ // If there are below, it cannot replace with arrow functions merely.
+ ThisExpression() {
+ const info = stack[stack.length - 1];
+
+ if (info) {
+ info.this = true;
+ }
+ },
+
+ Super() {
+ const info = stack[stack.length - 1];
+
+ if (info) {
+ info.super = true;
+ }
+ },
+
+ MetaProperty(node) {
+ const info = stack[stack.length - 1];
+
+ if (info && checkMetaProperty(node, "new", "target")) {
+ info.meta = true;
+ }
+ },
+
+ // To skip nested scopes.
+ FunctionDeclaration: enterScope,
+ "FunctionDeclaration:exit": exitScope,
+
+ // Main.
+ FunctionExpression: enterScope,
+ "FunctionExpression:exit"(node) {
+ const scopeInfo = exitScope();
+
+ // Skip named function expressions
+ if (allowNamedFunctions && node.id && node.id.name) {
+ return;
+ }
+
+ // Skip generators.
+ if (node.generator) {
+ return;
+ }
+
+ // Skip recursive functions.
+ const nameVar = context.getDeclaredVariables(node)[0];
+
+ if (isFunctionName(nameVar) && nameVar.references.length > 0) {
+ return;
+ }
+
+ // Skip if it's using arguments.
+ const variable = getVariableOfArguments(context.getScope());
+
+ if (variable && variable.references.length > 0) {
+ return;
+ }
+
+ // Reports if it's a callback which can replace with arrows.
+ const callbackInfo = getCallbackInfo(node);
+
+ if (callbackInfo.isCallback &&
+ (!allowUnboundThis || !scopeInfo.this || callbackInfo.isLexicalThis) &&
+ !scopeInfo.super &&
+ !scopeInfo.meta
+ ) {
+ context.report({
+ node,
+ message: "Unexpected function expression.",
+ fix(fixer) {
+ if ((!callbackInfo.isLexicalThis && scopeInfo.this) || hasDuplicateParams(node.params)) {
+
+ /*
+ * If the callback function does not have .bind(this) and contains a reference to `this`, there
+ * is no way to determine what `this` should be, so don't perform any fixes.
+ * If the callback function has duplicates in its list of parameters (possible in sloppy mode),
+ * don't replace it with an arrow function, because this is a SyntaxError with arrow functions.
+ */
+ return null;
+ }
+
+ const paramsLeftParen = node.params.length ? sourceCode.getTokenBefore(node.params[0]) : sourceCode.getTokenBefore(node.body, 1);
+ const paramsRightParen = sourceCode.getTokenBefore(node.body);
+ const asyncKeyword = node.async ? "async " : "";
+ const paramsFullText = sourceCode.text.slice(paramsLeftParen.range[0], paramsRightParen.range[1]);
+ const arrowFunctionText = `${asyncKeyword}${paramsFullText} => ${sourceCode.getText(node.body)}`;
+
+ /*
+ * If the callback function has `.bind(this)`, replace it with an arrow function and remove the binding.
+ * Otherwise, just replace the arrow function itself.
+ */
+ const replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node;
+
+ /*
+ * If the replaced node is part of a BinaryExpression, LogicalExpression, or MemberExpression, then
+ * the arrow function needs to be parenthesized, because `foo || () => {}` is invalid syntax even
+ * though `foo || function() {}` is valid.
+ */
+ const needsParens = replacedNode.parent.type !== "CallExpression" && replacedNode.parent.type !== "ConditionalExpression";
+ const replacementText = needsParens ? `(${arrowFunctionText})` : arrowFunctionText;
+
+ return fixer.replaceText(replacedNode, replacementText);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-const.js b/tools/node_modules/eslint/lib/rules/prefer-const.js
new file mode 100644
index 0000000000..a8cf3b7ef6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-const.js
@@ -0,0 +1,321 @@
+/**
+ * @fileoverview A rule to suggest using of const declaration for variables that are never reassigned after declared.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/;
+const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/;
+const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/;
+
+/**
+ * Adds multiple items to the tail of an array.
+ *
+ * @param {any[]} array - A destination to add.
+ * @param {any[]} values - Items to be added.
+ * @returns {void}
+ */
+const pushAll = Function.apply.bind(Array.prototype.push);
+
+/**
+ * Checks whether a given node is located at `ForStatement.init` or not.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is located at `ForStatement.init`.
+ */
+function isInitOfForStatement(node) {
+ return node.parent.type === "ForStatement" && node.parent.init === node;
+}
+
+/**
+ * Checks whether a given Identifier node becomes a VariableDeclaration or not.
+ *
+ * @param {ASTNode} identifier - An Identifier node to check.
+ * @returns {boolean} `true` if the node can become a VariableDeclaration.
+ */
+function canBecomeVariableDeclaration(identifier) {
+ let node = identifier.parent;
+
+ while (PATTERN_TYPE.test(node.type)) {
+ node = node.parent;
+ }
+
+ return (
+ node.type === "VariableDeclarator" ||
+ (
+ node.type === "AssignmentExpression" &&
+ node.parent.type === "ExpressionStatement" &&
+ DECLARATION_HOST_TYPE.test(node.parent.parent.type)
+ )
+ );
+}
+
+/**
+ * Gets an identifier node of a given variable.
+ *
+ * If the initialization exists or one or more reading references exist before
+ * the first assignment, the identifier node is the node of the declaration.
+ * Otherwise, the identifier node is the node of the first assignment.
+ *
+ * If the variable should not change to const, this function returns null.
+ * - If the variable is reassigned.
+ * - If the variable is never initialized nor assigned.
+ * - If the variable is initialized in a different scope from the declaration.
+ * - If the unique assignment of the variable cannot change to a declaration.
+ * e.g. `if (a) b = 1` / `return (b = 1)`
+ * - If the variable is declared in the global scope and `eslintUsed` is `true`.
+ * `/*exported foo` directive comment makes such variables. This rule does not
+ * warn such variables because this rule cannot distinguish whether the
+ * exported variables are reassigned or not.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to get.
+ * @param {boolean} ignoreReadBeforeAssign -
+ * The value of `ignoreReadBeforeAssign` option.
+ * @returns {ASTNode|null}
+ * An Identifier node if the variable should change to const.
+ * Otherwise, null.
+ */
+function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
+ if (variable.eslintUsed && variable.scope.type === "global") {
+ return null;
+ }
+
+ // Finds the unique WriteReference.
+ let writer = null;
+ let isReadBeforeInit = false;
+ const references = variable.references;
+
+ for (let i = 0; i < references.length; ++i) {
+ const reference = references[i];
+
+ if (reference.isWrite()) {
+ const isReassigned = (
+ writer !== null &&
+ writer.identifier !== reference.identifier
+ );
+
+ if (isReassigned) {
+ return null;
+ }
+ writer = reference;
+
+ } else if (reference.isRead() && writer === null) {
+ if (ignoreReadBeforeAssign) {
+ return null;
+ }
+ isReadBeforeInit = true;
+ }
+ }
+
+ /*
+ * If the assignment is from a different scope, ignore it.
+ * If the assignment cannot change to a declaration, ignore it.
+ */
+ const shouldBeConst = (
+ writer !== null &&
+ writer.from === variable.scope &&
+ canBecomeVariableDeclaration(writer.identifier)
+ );
+
+ if (!shouldBeConst) {
+ return null;
+ }
+ if (isReadBeforeInit) {
+ return variable.defs[0].name;
+ }
+ return writer.identifier;
+}
+
+/**
+ * Gets the VariableDeclarator/AssignmentExpression node that a given reference
+ * belongs to.
+ * This is used to detect a mix of reassigned and never reassigned in a
+ * destructuring.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to get.
+ * @returns {ASTNode|null} A VariableDeclarator/AssignmentExpression node or
+ * null.
+ */
+function getDestructuringHost(reference) {
+ if (!reference.isWrite()) {
+ return null;
+ }
+ let node = reference.identifier.parent;
+
+ while (PATTERN_TYPE.test(node.type)) {
+ node = node.parent;
+ }
+
+ if (!DESTRUCTURING_HOST_TYPE.test(node.type)) {
+ return null;
+ }
+ return node;
+}
+
+/**
+ * Groups by the VariableDeclarator/AssignmentExpression node that each
+ * reference of given variables belongs to.
+ * This is used to detect a mix of reassigned and never reassigned in a
+ * destructuring.
+ *
+ * @param {eslint-scope.Variable[]} variables - Variables to group by destructuring.
+ * @param {boolean} ignoreReadBeforeAssign -
+ * The value of `ignoreReadBeforeAssign` option.
+ * @returns {Map<ASTNode, ASTNode[]>} Grouped identifier nodes.
+ */
+function groupByDestructuring(variables, ignoreReadBeforeAssign) {
+ const identifierMap = new Map();
+
+ for (let i = 0; i < variables.length; ++i) {
+ const variable = variables[i];
+ const references = variable.references;
+ const identifier = getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign);
+ let prevId = null;
+
+ for (let j = 0; j < references.length; ++j) {
+ const reference = references[j];
+ const id = reference.identifier;
+
+ /*
+ * Avoid counting a reference twice or more for default values of
+ * destructuring.
+ */
+ if (id === prevId) {
+ continue;
+ }
+ prevId = id;
+
+ // Add the identifier node into the destructuring group.
+ const group = getDestructuringHost(reference);
+
+ if (group) {
+ if (identifierMap.has(group)) {
+ identifierMap.get(group).push(identifier);
+ } else {
+ identifierMap.set(group, [identifier]);
+ }
+ }
+ }
+ }
+
+ return identifierMap;
+}
+
+/**
+ * Finds the nearest parent of node with a given type.
+ *
+ * @param {ASTNode} node – The node to search from.
+ * @param {string} type – The type field of the parent node.
+ * @param {Function} shouldStop – a predicate that returns true if the traversal should stop, and false otherwise.
+ * @returns {ASTNode} The closest ancestor with the specified type; null if no such ancestor exists.
+ */
+function findUp(node, type, shouldStop) {
+ if (!node || shouldStop(node)) {
+ return null;
+ }
+ if (node.type === type) {
+ return node;
+ }
+ return findUp(node.parent, type, shouldStop);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `const` declarations for variables that are never reassigned after declared",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ destructuring: { enum: ["any", "all"] },
+ ignoreReadBeforeAssign: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const sourceCode = context.getSourceCode();
+ const checkingMixedDestructuring = options.destructuring !== "all";
+ const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true;
+ const variables = [];
+
+ /**
+ * Reports given identifier nodes if all of the nodes should be declared
+ * as const.
+ *
+ * The argument 'nodes' is an array of Identifier nodes.
+ * This node is the result of 'getIdentifierIfShouldBeConst()', so it's
+ * nullable. In simple declaration or assignment cases, the length of
+ * the array is 1. In destructuring cases, the length of the array can
+ * be 2 or more.
+ *
+ * @param {(eslint-scope.Reference|null)[]} nodes -
+ * References which are grouped by destructuring to report.
+ * @returns {void}
+ */
+ function checkGroup(nodes) {
+ const nodesToReport = nodes.filter(Boolean);
+
+ if (nodes.length && (checkingMixedDestructuring || nodesToReport.length === nodes.length)) {
+ const varDeclParent = findUp(nodes[0], "VariableDeclaration", parentNode => parentNode.type.endsWith("Statement"));
+ const shouldFix = varDeclParent &&
+
+ /*
+ * If there are multiple variable declarations, like {let a = 1, b = 2}, then
+ * do not attempt to fix if one of the declarations should be `const`. It's
+ * too hard to know how the developer would want to automatically resolve the issue.
+ */
+ varDeclParent.declarations.length === 1 &&
+
+ // Don't do a fix unless the variable is initialized (or it's in a for-in or for-of loop)
+ (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" || varDeclParent.declarations[0].init) &&
+
+ /*
+ * If options.destucturing is "all", then this warning will not occur unless
+ * every assignment in the destructuring should be const. In that case, it's safe
+ * to apply the fix.
+ */
+ nodesToReport.length === nodes.length;
+
+ nodesToReport.forEach(node => {
+ context.report({
+ node,
+ message: "'{{name}}' is never reassigned. Use 'const' instead.",
+ data: node,
+ fix: shouldFix ? fixer => fixer.replaceText(sourceCode.getFirstToken(varDeclParent), "const") : null
+ });
+ });
+ }
+ }
+
+ return {
+ "Program:exit"() {
+ groupByDestructuring(variables, ignoreReadBeforeAssign).forEach(checkGroup);
+ },
+
+ VariableDeclaration(node) {
+ if (node.kind === "let" && !isInitOfForStatement(node)) {
+ pushAll(variables, context.getDeclaredVariables(node));
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-destructuring.js b/tools/node_modules/eslint/lib/rules/prefer-destructuring.js
new file mode 100644
index 0000000000..56c348a478
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-destructuring.js
@@ -0,0 +1,217 @@
+/**
+ * @fileoverview Prefer destructuring from arrays and objects
+ * @author Alex LaFroscia
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require destructuring from arrays and/or objects",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+ schema: [
+ {
+
+ /*
+ * old support {array: Boolean, object: Boolean}
+ * new support {VariableDeclarator: {}, AssignmentExpression: {}}
+ */
+ oneOf: [
+ {
+ type: "object",
+ properties: {
+ VariableDeclarator: {
+ type: "object",
+ properties: {
+ array: {
+ type: "boolean"
+ },
+ object: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ },
+ AssignmentExpression: {
+ type: "object",
+ properties: {
+ array: {
+ type: "boolean"
+ },
+ object: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ },
+ {
+ type: "object",
+ properties: {
+ array: {
+ type: "boolean"
+ },
+ object: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ {
+ type: "object",
+ properties: {
+ enforceForRenamedProperties: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+ create(context) {
+
+ const enabledTypes = context.options[0];
+ const enforceForRenamedProperties = context.options[1] && context.options[1].enforceForRenamedProperties;
+ let normalizedOptions = {
+ VariableDeclarator: { array: true, object: true },
+ AssignmentExpression: { array: true, object: true }
+ };
+
+ if (enabledTypes) {
+ normalizedOptions = typeof enabledTypes.array !== "undefined" || typeof enabledTypes.object !== "undefined"
+ ? { VariableDeclarator: enabledTypes, AssignmentExpression: enabledTypes }
+ : enabledTypes;
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @param {string} nodeType "AssignmentExpression" or "VariableDeclarator"
+ * @param {string} destructuringType "array" or "object"
+ * @returns {boolean} `true` if the destructuring type should be checked for the given node
+ */
+ function shouldCheck(nodeType, destructuringType) {
+ return normalizedOptions &&
+ normalizedOptions[nodeType] &&
+ normalizedOptions[nodeType][destructuringType];
+ }
+
+ /**
+ * Determines if the given node is accessing an array index
+ *
+ * This is used to differentiate array index access from object property
+ * access.
+ *
+ * @param {ASTNode} node the node to evaluate
+ * @returns {boolean} whether or not the node is an integer
+ */
+ function isArrayIndexAccess(node) {
+ return Number.isInteger(node.property.value);
+ }
+
+ /**
+ * Report that the given node should use destructuring
+ *
+ * @param {ASTNode} reportNode the node to report
+ * @param {string} type the type of destructuring that should have been done
+ * @returns {void}
+ */
+ function report(reportNode, type) {
+ context.report({ node: reportNode, message: "Use {{type}} destructuring.", data: { type } });
+ }
+
+ /**
+ * Check that the `prefer-destructuring` rules are followed based on the
+ * given left- and right-hand side of the assignment.
+ *
+ * Pulled out into a separate method so that VariableDeclarators and
+ * AssignmentExpressions can share the same verification logic.
+ *
+ * @param {ASTNode} leftNode the left-hand side of the assignment
+ * @param {ASTNode} rightNode the right-hand side of the assignment
+ * @param {ASTNode} reportNode the node to report the error on
+ * @returns {void}
+ */
+ function performCheck(leftNode, rightNode, reportNode) {
+ if (rightNode.type !== "MemberExpression" || rightNode.object.type === "Super") {
+ return;
+ }
+
+ if (isArrayIndexAccess(rightNode)) {
+ if (shouldCheck(reportNode.type, "array")) {
+ report(reportNode, "array");
+ }
+ return;
+ }
+
+ if (shouldCheck(reportNode.type, "object") && enforceForRenamedProperties) {
+ report(reportNode, "object");
+ return;
+ }
+
+ if (shouldCheck(reportNode.type, "object")) {
+ const property = rightNode.property;
+
+ if ((property.type === "Literal" && leftNode.name === property.value) || (property.type === "Identifier" &&
+ leftNode.name === property.name)) {
+ report(reportNode, "object");
+ }
+ }
+ }
+
+ /**
+ * Check if a given variable declarator is coming from an property access
+ * that should be using destructuring instead
+ *
+ * @param {ASTNode} node the variable declarator to check
+ * @returns {void}
+ */
+ function checkVariableDeclarator(node) {
+
+ // Skip if variable is declared without assignment
+ if (!node.init) {
+ return;
+ }
+
+ // We only care about member expressions past this point
+ if (node.init.type !== "MemberExpression") {
+ return;
+ }
+
+ performCheck(node.id, node.init, node);
+ }
+
+ /**
+ * Run the `prefer-destructuring` check on an AssignmentExpression
+ *
+ * @param {ASTNode} node the AssignmentExpression node
+ * @returns {void}
+ */
+ function checkAssigmentExpression(node) {
+ if (node.operator === "=") {
+ performCheck(node.left, node.right, node);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ VariableDeclarator: checkVariableDeclarator,
+ AssignmentExpression: checkAssigmentExpression
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-numeric-literals.js b/tools/node_modules/eslint/lib/rules/prefer-numeric-literals.js
new file mode 100644
index 0000000000..929e660c66
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-numeric-literals.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
+ * @author Annie Zhang, Henry Zhu
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks to see if a CallExpression's callee node is `parseInt` or
+ * `Number.parseInt`.
+ * @param {ASTNode} calleeNode The callee node to evaluate.
+ * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
+ * false otherwise.
+ */
+function isParseInt(calleeNode) {
+ switch (calleeNode.type) {
+ case "Identifier":
+ return calleeNode.name === "parseInt";
+ case "MemberExpression":
+ return calleeNode.object.type === "Identifier" &&
+ calleeNode.object.name === "Number" &&
+ calleeNode.property.type === "Identifier" &&
+ calleeNode.property.name === "parseInt";
+
+ // no default
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ const radixMap = {
+ 2: "binary",
+ 8: "octal",
+ 16: "hexadecimal"
+ };
+
+ const prefixMap = {
+ 2: "0b",
+ 8: "0o",
+ 16: "0x"
+ };
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+
+ CallExpression(node) {
+
+ // doesn't check parseInt() if it doesn't have a radix argument
+ if (node.arguments.length !== 2) {
+ return;
+ }
+
+ // only error if the radix is 2, 8, or 16
+ const radixName = radixMap[node.arguments[1].value];
+
+ if (isParseInt(node.callee) &&
+ radixName &&
+ node.arguments[0].type === "Literal"
+ ) {
+ context.report({
+ node,
+ message: "Use {{radixName}} literals instead of {{functionName}}().",
+ data: {
+ radixName,
+ functionName: sourceCode.getText(node.callee)
+ },
+ fix(fixer) {
+ const newPrefix = prefixMap[node.arguments[1].value];
+
+ if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
+
+ /*
+ * If the newly-produced literal would be invalid, (e.g. 0b1234),
+ * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
+ */
+ return null;
+ }
+ return fixer.replaceText(node, prefixMap[node.arguments[1].value] + node.arguments[0].value);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-promise-reject-errors.js b/tools/node_modules/eslint/lib/rules/prefer-promise-reject-errors.js
new file mode 100644
index 0000000000..d2a6b5df10
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-promise-reject-errors.js
@@ -0,0 +1,124 @@
+/**
+ * @fileoverview restrict values that can be used as Promise rejection reasons
+ * @author Teddy Katz
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require using Error objects as Promise rejection reasons",
+ category: "Best Practices",
+ recommended: false
+ },
+ fixable: null,
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowEmptyReject: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const ALLOW_EMPTY_REJECT = context.options.length && context.options[0].allowEmptyReject;
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ /**
+ * Checks the argument of a reject() or Promise.reject() CallExpression, and reports it if it can't be an Error
+ * @param {ASTNode} callExpression A CallExpression node which is used to reject a Promise
+ * @returns {void}
+ */
+ function checkRejectCall(callExpression) {
+ if (!callExpression.arguments.length && ALLOW_EMPTY_REJECT) {
+ return;
+ }
+ if (
+ !callExpression.arguments.length ||
+ !astUtils.couldBeError(callExpression.arguments[0]) ||
+ callExpression.arguments[0].type === "Identifier" && callExpression.arguments[0].name === "undefined"
+ ) {
+ context.report({
+ node: callExpression,
+ message: "Expected the Promise rejection reason to be an Error."
+ });
+ }
+ }
+
+ /**
+ * Determines whether a function call is a Promise.reject() call
+ * @param {ASTNode} node A CallExpression node
+ * @returns {boolean} `true` if the call is a Promise.reject() call
+ */
+ function isPromiseRejectCall(node) {
+ return node.callee.type === "MemberExpression" &&
+ node.callee.object.type === "Identifier" && node.callee.object.name === "Promise" &&
+ node.callee.property.type === "Identifier" && node.callee.property.name === "reject";
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ return {
+
+ // Check `Promise.reject(value)` calls.
+ CallExpression(node) {
+ if (isPromiseRejectCall(node)) {
+ checkRejectCall(node);
+ }
+ },
+
+ /*
+ * Check for `new Promise((resolve, reject) => {})`, and check for reject() calls.
+ * This function is run on "NewExpression:exit" instead of "NewExpression" to ensure that
+ * the nodes in the expression already have the `parent` property.
+ */
+ "NewExpression:exit"(node) {
+ if (
+ node.callee.type === "Identifier" && node.callee.name === "Promise" &&
+ node.arguments.length && astUtils.isFunction(node.arguments[0]) &&
+ node.arguments[0].params.length > 1 && node.arguments[0].params[1].type === "Identifier"
+ ) {
+ context.getDeclaredVariables(node.arguments[0])
+
+ /*
+ * Find the first variable that matches the second parameter's name.
+ * If the first parameter has the same name as the second parameter, then the variable will actually
+ * be "declared" when the first parameter is evaluated, but then it will be immediately overwritten
+ * by the second parameter. It's not possible for an expression with the variable to be evaluated before
+ * the variable is overwritten, because functions with duplicate parameters cannot have destructuring or
+ * default assignments in their parameter lists. Therefore, it's not necessary to explicitly account for
+ * this case.
+ */
+ .find(variable => variable.name === node.arguments[0].params[1].name)
+
+ // Get the references to that variable.
+ .references
+
+ // Only check the references that read the parameter's value.
+ .filter(ref => ref.isRead())
+
+ // Only check the references that are used as the callee in a function call, e.g. `reject(foo)`.
+ .filter(ref => ref.identifier.parent.type === "CallExpression" && ref.identifier === ref.identifier.parent.callee)
+
+ // Check the argument of the function call to determine whether it's an Error.
+ .forEach(ref => checkRejectCall(ref.identifier.parent));
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-reflect.js b/tools/node_modules/eslint/lib/rules/prefer-reflect.js
new file mode 100644
index 0000000000..a47e66c5f5
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-reflect.js
@@ -0,0 +1,119 @@
+/**
+ * @fileoverview Rule to suggest using "Reflect" api over Function/Object methods
+ * @author Keith Cirkel <http://keithcirkel.co.uk>
+ * @deprecated in ESLint v3.9.0
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `Reflect` methods where applicable",
+ category: "ECMAScript 6",
+ recommended: false,
+ replacedBy: []
+ },
+
+ deprecated: true,
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: {
+ enum: [
+ "apply",
+ "call",
+ "delete",
+ "defineProperty",
+ "getOwnPropertyDescriptor",
+ "getPrototypeOf",
+ "setPrototypeOf",
+ "isExtensible",
+ "getOwnPropertyNames",
+ "preventExtensions"
+ ]
+ },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const existingNames = {
+ apply: "Function.prototype.apply",
+ call: "Function.prototype.call",
+ defineProperty: "Object.defineProperty",
+ getOwnPropertyDescriptor: "Object.getOwnPropertyDescriptor",
+ getPrototypeOf: "Object.getPrototypeOf",
+ setPrototypeOf: "Object.setPrototypeOf",
+ isExtensible: "Object.isExtensible",
+ getOwnPropertyNames: "Object.getOwnPropertyNames",
+ preventExtensions: "Object.preventExtensions"
+ };
+
+ const reflectSubsitutes = {
+ apply: "Reflect.apply",
+ call: "Reflect.apply",
+ defineProperty: "Reflect.defineProperty",
+ getOwnPropertyDescriptor: "Reflect.getOwnPropertyDescriptor",
+ getPrototypeOf: "Reflect.getPrototypeOf",
+ setPrototypeOf: "Reflect.setPrototypeOf",
+ isExtensible: "Reflect.isExtensible",
+ getOwnPropertyNames: "Reflect.getOwnPropertyNames",
+ preventExtensions: "Reflect.preventExtensions"
+ };
+
+ const exceptions = (context.options[0] || {}).exceptions || [];
+
+ /**
+ * Reports the Reflect violation based on the `existing` and `substitute`
+ * @param {Object} node The node that violates the rule.
+ * @param {string} existing The existing method name that has been used.
+ * @param {string} substitute The Reflect substitute that should be used.
+ * @returns {void}
+ */
+ function report(node, existing, substitute) {
+ context.report({
+ node,
+ message: "Avoid using {{existing}}, instead use {{substitute}}.",
+ data: {
+ existing,
+ substitute
+ }
+ });
+ }
+
+ return {
+ CallExpression(node) {
+ const methodName = (node.callee.property || {}).name;
+ const isReflectCall = (node.callee.object || {}).name === "Reflect";
+ const hasReflectSubsitute = reflectSubsitutes.hasOwnProperty(methodName);
+ const userConfiguredException = exceptions.indexOf(methodName) !== -1;
+
+ if (hasReflectSubsitute && !isReflectCall && !userConfiguredException) {
+ report(node, existingNames[methodName], reflectSubsitutes[methodName]);
+ }
+ },
+ UnaryExpression(node) {
+ const isDeleteOperator = node.operator === "delete";
+ const targetsIdentifier = node.argument.type === "Identifier";
+ const userConfiguredException = exceptions.indexOf("delete") !== -1;
+
+ if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) {
+ report(node, "the delete keyword", "Reflect.deleteProperty");
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-rest-params.js b/tools/node_modules/eslint/lib/rules/prefer-rest-params.js
new file mode 100644
index 0000000000..03342371b2
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-rest-params.js
@@ -0,0 +1,111 @@
+/**
+ * @fileoverview Rule to
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets the variable object of `arguments` which is defined implicitly.
+ * @param {eslint-scope.Scope} scope - A scope to get.
+ * @returns {eslint-scope.Variable} The found variable object.
+ */
+function getVariableOfArguments(scope) {
+ const variables = scope.variables;
+
+ for (let i = 0; i < variables.length; ++i) {
+ const variable = variables[i];
+
+ if (variable.name === "arguments") {
+
+ /*
+ * If there was a parameter which is named "arguments", the implicit "arguments" is not defined.
+ * So does fast return with null.
+ */
+ return (variable.identifiers.length === 0) ? variable : null;
+ }
+ }
+
+ /* istanbul ignore next : unreachable */
+ return null;
+}
+
+/**
+ * Checks if the given reference is not normal member access.
+ *
+ * - arguments .... true // not member access
+ * - arguments[i] .... true // computed member access
+ * - arguments[0] .... true // computed member access
+ * - arguments.length .... false // normal member access
+ *
+ * @param {eslint-scope.Reference} reference - The reference to check.
+ * @returns {boolean} `true` if the reference is not normal member access.
+ */
+function isNotNormalMemberAccess(reference) {
+ const id = reference.identifier;
+ const parent = id.parent;
+
+ return !(
+ parent.type === "MemberExpression" &&
+ parent.object === id &&
+ !parent.computed
+ );
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require rest parameters instead of `arguments`",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Reports a given reference.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to report.
+ * @returns {void}
+ */
+ function report(reference) {
+ context.report({
+ node: reference.identifier,
+ loc: reference.identifier.loc,
+ message: "Use the rest parameters instead of 'arguments'."
+ });
+ }
+
+ /**
+ * Reports references of the implicit `arguments` variable if exist.
+ *
+ * @returns {void}
+ */
+ function checkForArguments() {
+ const argumentsVar = getVariableOfArguments(context.getScope());
+
+ if (argumentsVar) {
+ argumentsVar
+ .references
+ .filter(isNotNormalMemberAccess)
+ .forEach(report);
+ }
+ }
+
+ return {
+ "FunctionDeclaration:exit": checkForArguments,
+ "FunctionExpression:exit": checkForArguments
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-spread.js b/tools/node_modules/eslint/lib/rules/prefer-spread.js
new file mode 100644
index 0000000000..c111d5f98e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-spread.js
@@ -0,0 +1,96 @@
+/**
+ * @fileoverview A rule to suggest using of the spread operator instead of `.apply()`.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a node is a `.apply()` for variadic.
+ * @param {ASTNode} node - A CallExpression node to check.
+ * @returns {boolean} Whether or not the node is a `.apply()` for variadic.
+ */
+function isVariadicApplyCalling(node) {
+ return (
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.property.name === "apply" &&
+ node.callee.computed === false &&
+ node.arguments.length === 2 &&
+ node.arguments[1].type !== "ArrayExpression" &&
+ node.arguments[1].type !== "SpreadElement"
+ );
+}
+
+
+/**
+ * Checks whether or not `thisArg` is not changed by `.apply()`.
+ * @param {ASTNode|null} expectedThis - The node that is the owner of the applied function.
+ * @param {ASTNode} thisArg - The node that is given to the first argument of the `.apply()`.
+ * @param {RuleContext} context - The ESLint rule context object.
+ * @returns {boolean} Whether or not `thisArg` is not changed by `.apply()`.
+ */
+function isValidThisArg(expectedThis, thisArg, context) {
+ if (!expectedThis) {
+ return astUtils.isNullOrUndefined(thisArg);
+ }
+ return astUtils.equalTokens(expectedThis, thisArg, context);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require spread operators instead of `.apply()`",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+ CallExpression(node) {
+ if (!isVariadicApplyCalling(node)) {
+ return;
+ }
+
+ const applied = node.callee.object;
+ const expectedThis = (applied.type === "MemberExpression") ? applied.object : null;
+ const thisArg = node.arguments[0];
+
+ if (isValidThisArg(expectedThis, thisArg, sourceCode)) {
+ context.report({
+ node,
+ message: "Use the spread operator instead of '.apply()'.",
+ fix(fixer) {
+ if (expectedThis && expectedThis.type !== "Identifier") {
+
+ // Don't fix cases where the `this` value could be a computed expression.
+ return null;
+ }
+
+ const propertyDot = sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === ".");
+
+ return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/prefer-template.js b/tools/node_modules/eslint/lib/rules/prefer-template.js
new file mode 100644
index 0000000000..076ce6a3ea
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/prefer-template.js
@@ -0,0 +1,232 @@
+/**
+ * @fileoverview A rule to suggest using template literals instead of string concatenation.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks whether or not a given node is a concatenation.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a concatenation.
+ */
+function isConcatenation(node) {
+ return node.type === "BinaryExpression" && node.operator === "+";
+}
+
+/**
+ * Gets the top binary expression node for concatenation in parents of a given node.
+ * @param {ASTNode} node - A node to get.
+ * @returns {ASTNode} the top binary expression node in parents of a given node.
+ */
+function getTopConcatBinaryExpression(node) {
+ while (isConcatenation(node.parent)) {
+ node = node.parent;
+ }
+ return node;
+}
+
+/**
+ * Checks whether or not a given binary expression has string literals.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node has string literals.
+ */
+function hasStringLiteral(node) {
+ if (isConcatenation(node)) {
+
+ // `left` is deeper than `right` normally.
+ return hasStringLiteral(node.right) || hasStringLiteral(node.left);
+ }
+ return astUtils.isStringLiteral(node);
+}
+
+/**
+ * Checks whether or not a given binary expression has non string literals.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node has non string literals.
+ */
+function hasNonStringLiteral(node) {
+ if (isConcatenation(node)) {
+
+ // `left` is deeper than `right` normally.
+ return hasNonStringLiteral(node.right) || hasNonStringLiteral(node.left);
+ }
+ return !astUtils.isStringLiteral(node);
+}
+
+/**
+ * Determines whether a given node will start with a template curly expression (`${}`) when being converted to a template literal.
+ * @param {ASTNode} node The node that will be fixed to a template literal
+ * @returns {boolean} `true` if the node will start with a template curly.
+ */
+function startsWithTemplateCurly(node) {
+ if (node.type === "BinaryExpression") {
+ return startsWithTemplateCurly(node.left);
+ }
+ if (node.type === "TemplateLiteral") {
+ return node.expressions.length && node.quasis.length && node.quasis[0].range[0] === node.quasis[0].range[1];
+ }
+ return node.type !== "Literal" || typeof node.value !== "string";
+}
+
+/**
+ * Determines whether a given node end with a template curly expression (`${}`) when being converted to a template literal.
+ * @param {ASTNode} node The node that will be fixed to a template literal
+ * @returns {boolean} `true` if the node will end with a template curly.
+ */
+function endsWithTemplateCurly(node) {
+ if (node.type === "BinaryExpression") {
+ return startsWithTemplateCurly(node.right);
+ }
+ if (node.type === "TemplateLiteral") {
+ return node.expressions.length && node.quasis.length && node.quasis[node.quasis.length - 1].range[0] === node.quasis[node.quasis.length - 1].range[1];
+ }
+ return node.type !== "Literal" || typeof node.value !== "string";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require template literals instead of string concatenation",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ let done = Object.create(null);
+
+ /**
+ * Gets the non-token text between two nodes, ignoring any other tokens that appear between the two tokens.
+ * @param {ASTNode} node1 The first node
+ * @param {ASTNode} node2 The second node
+ * @returns {string} The text between the nodes, excluding other tokens
+ */
+ function getTextBetween(node1, node2) {
+ const allTokens = [node1].concat(sourceCode.getTokensBetween(node1, node2)).concat(node2);
+ const sourceText = sourceCode.getText();
+
+ return allTokens.slice(0, -1).reduce((accumulator, token, index) => accumulator + sourceText.slice(token.range[1], allTokens[index + 1].range[0]), "");
+ }
+
+ /**
+ * Returns a template literal form of the given node.
+ * @param {ASTNode} currentNode A node that should be converted to a template literal
+ * @param {string} textBeforeNode Text that should appear before the node
+ * @param {string} textAfterNode Text that should appear after the node
+ * @returns {string} A string form of this node, represented as a template literal
+ */
+ function getTemplateLiteral(currentNode, textBeforeNode, textAfterNode) {
+ if (currentNode.type === "Literal" && typeof currentNode.value === "string") {
+
+ /*
+ * If the current node is a string literal, escape any instances of ${ or ` to prevent them from being interpreted
+ * as a template placeholder. However, if the code already contains a backslash before the ${ or `
+ * for some reason, don't add another backslash, because that would change the meaning of the code (it would cause
+ * an actual backslash character to appear before the dollar sign).
+ */
+ return `\`${currentNode.raw.slice(1, -1).replace(/\\*(\${|`)/g, matched => {
+ if (matched.lastIndexOf("\\") % 2) {
+ return `\\${matched}`;
+ }
+ return matched;
+
+ // Unescape any quotes that appear in the original Literal that no longer need to be escaped.
+ }).replace(new RegExp(`\\\\${currentNode.raw[0]}`, "g"), currentNode.raw[0])}\``;
+ }
+
+ if (currentNode.type === "TemplateLiteral") {
+ return sourceCode.getText(currentNode);
+ }
+
+ if (isConcatenation(currentNode) && hasStringLiteral(currentNode) && hasNonStringLiteral(currentNode)) {
+ const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");
+ const textBeforePlus = getTextBetween(currentNode.left, plusSign);
+ const textAfterPlus = getTextBetween(plusSign, currentNode.right);
+ const leftEndsWithCurly = endsWithTemplateCurly(currentNode.left);
+ const rightStartsWithCurly = startsWithTemplateCurly(currentNode.right);
+
+ if (leftEndsWithCurly) {
+
+ // If the left side of the expression ends with a template curly, add the extra text to the end of the curly bracket.
+ // `foo${bar}` /* comment */ + 'baz' --> `foo${bar /* comment */ }${baz}`
+ return getTemplateLiteral(currentNode.left, textBeforeNode, textBeforePlus + textAfterPlus).slice(0, -1) +
+ getTemplateLiteral(currentNode.right, null, textAfterNode).slice(1);
+ }
+ if (rightStartsWithCurly) {
+
+ // Otherwise, if the right side of the expression starts with a template curly, add the text there.
+ // 'foo' /* comment */ + `${bar}baz` --> `foo${ /* comment */ bar}baz`
+ return getTemplateLiteral(currentNode.left, textBeforeNode, null).slice(0, -1) +
+ getTemplateLiteral(currentNode.right, textBeforePlus + textAfterPlus, textAfterNode).slice(1);
+ }
+
+ /*
+ * Otherwise, these nodes should not be combined into a template curly, since there is nowhere to put
+ * the text between them.
+ */
+ return `${getTemplateLiteral(currentNode.left, textBeforeNode, null)}${textBeforePlus}+${textAfterPlus}${getTemplateLiteral(currentNode.right, textAfterNode, null)}`;
+ }
+
+ return `\`\${${textBeforeNode || ""}${sourceCode.getText(currentNode)}${textAfterNode || ""}}\``;
+ }
+
+ /**
+ * Reports if a given node is string concatenation with non string literals.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {void}
+ */
+ function checkForStringConcat(node) {
+ if (!astUtils.isStringLiteral(node) || !isConcatenation(node.parent)) {
+ return;
+ }
+
+ const topBinaryExpr = getTopConcatBinaryExpression(node.parent);
+
+ // Checks whether or not this node had been checked already.
+ if (done[topBinaryExpr.range[0]]) {
+ return;
+ }
+ done[topBinaryExpr.range[0]] = true;
+
+ if (hasNonStringLiteral(topBinaryExpr)) {
+ context.report({
+ node: topBinaryExpr,
+ message: "Unexpected string concatenation.",
+ fix(fixer) {
+ return fixer.replaceText(topBinaryExpr, getTemplateLiteral(topBinaryExpr, null, null));
+ }
+ });
+ }
+ }
+
+ return {
+ Program() {
+ done = Object.create(null);
+ },
+
+ Literal: checkForStringConcat,
+ TemplateLiteral: checkForStringConcat
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/quote-props.js b/tools/node_modules/eslint/lib/rules/quote-props.js
new file mode 100644
index 0000000000..6ac1f3c138
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/quote-props.js
@@ -0,0 +1,298 @@
+/**
+ * @fileoverview Rule to flag non-quoted property names in object literals.
+ * @author Mathias Bynens <http://mathiasbynens.be/>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const espree = require("espree"),
+ keywords = require("../util/keywords");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require quotes around object literal property names",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always", "as-needed", "consistent", "consistent-as-needed"]
+ }
+ ],
+ minItems: 0,
+ maxItems: 1
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always", "as-needed", "consistent", "consistent-as-needed"]
+ },
+ {
+ type: "object",
+ properties: {
+ keywords: {
+ type: "boolean"
+ },
+ unnecessary: {
+ type: "boolean"
+ },
+ numbers: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ },
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const MODE = context.options[0],
+ KEYWORDS = context.options[1] && context.options[1].keywords,
+ CHECK_UNNECESSARY = !context.options[1] || context.options[1].unnecessary !== false,
+ NUMBERS = context.options[1] && context.options[1].numbers,
+
+ MESSAGE_UNNECESSARY = "Unnecessarily quoted property '{{property}}' found.",
+ MESSAGE_UNQUOTED = "Unquoted property '{{property}}' found.",
+ MESSAGE_NUMERIC = "Unquoted number literal '{{property}}' used as key.",
+ MESSAGE_RESERVED = "Unquoted reserved word '{{property}}' used as key.",
+ sourceCode = context.getSourceCode();
+
+
+ /**
+ * Checks whether a certain string constitutes an ES3 token
+ * @param {string} tokenStr - The string to be checked.
+ * @returns {boolean} `true` if it is an ES3 token.
+ */
+ function isKeyword(tokenStr) {
+ return keywords.indexOf(tokenStr) >= 0;
+ }
+
+ /**
+ * Checks if an espree-tokenized key has redundant quotes (i.e. whether quotes are unnecessary)
+ * @param {string} rawKey The raw key value from the source
+ * @param {espreeTokens} tokens The espree-tokenized node key
+ * @param {boolean} [skipNumberLiterals=false] Indicates whether number literals should be checked
+ * @returns {boolean} Whether or not a key has redundant quotes.
+ * @private
+ */
+ function areQuotesRedundant(rawKey, tokens, skipNumberLiterals) {
+ return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length &&
+ (["Identifier", "Keyword", "Null", "Boolean"].indexOf(tokens[0].type) >= 0 ||
+ (tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value));
+ }
+
+ /**
+ * Returns a string representation of a property node with quotes removed
+ * @param {ASTNode} key Key AST Node, which may or may not be quoted
+ * @returns {string} A replacement string for this property
+ */
+ function getUnquotedKey(key) {
+ return key.type === "Identifier" ? key.name : key.value;
+ }
+
+ /**
+ * Returns a string representation of a property node with quotes added
+ * @param {ASTNode} key Key AST Node, which may or may not be quoted
+ * @returns {string} A replacement string for this property
+ */
+ function getQuotedKey(key) {
+ if (key.type === "Literal" && typeof key.value === "string") {
+
+ // If the key is already a string literal, don't replace the quotes with double quotes.
+ return sourceCode.getText(key);
+ }
+
+ // Otherwise, the key is either an identifier or a number literal.
+ return `"${key.type === "Identifier" ? key.name : key.value}"`;
+ }
+
+ /**
+ * Ensures that a property's key is quoted only when necessary
+ * @param {ASTNode} node Property AST node
+ * @returns {void}
+ */
+ function checkUnnecessaryQuotes(node) {
+ const key = node.key;
+
+ if (node.method || node.computed || node.shorthand) {
+ return;
+ }
+
+ if (key.type === "Literal" && typeof key.value === "string") {
+ let tokens;
+
+ try {
+ tokens = espree.tokenize(key.value);
+ } catch (e) {
+ return;
+ }
+
+ if (tokens.length !== 1) {
+ return;
+ }
+
+ const isKeywordToken = isKeyword(tokens[0].value);
+
+ if (isKeywordToken && KEYWORDS) {
+ return;
+ }
+
+ if (CHECK_UNNECESSARY && areQuotesRedundant(key.value, tokens, NUMBERS)) {
+ context.report({
+ node,
+ message: MESSAGE_UNNECESSARY,
+ data: { property: key.value },
+ fix: fixer => fixer.replaceText(key, getUnquotedKey(key))
+ });
+ }
+ } else if (KEYWORDS && key.type === "Identifier" && isKeyword(key.name)) {
+ context.report({
+ node,
+ message: MESSAGE_RESERVED,
+ data: { property: key.name },
+ fix: fixer => fixer.replaceText(key, getQuotedKey(key))
+ });
+ } else if (NUMBERS && key.type === "Literal" && typeof key.value === "number") {
+ context.report({
+ node,
+ message: MESSAGE_NUMERIC,
+ data: { property: key.value },
+ fix: fixer => fixer.replaceText(key, getQuotedKey(key))
+ });
+ }
+ }
+
+ /**
+ * Ensures that a property's key is quoted
+ * @param {ASTNode} node Property AST node
+ * @returns {void}
+ */
+ function checkOmittedQuotes(node) {
+ const key = node.key;
+
+ if (!node.method && !node.computed && !node.shorthand && !(key.type === "Literal" && typeof key.value === "string")) {
+ context.report({
+ node,
+ message: MESSAGE_UNQUOTED,
+ data: { property: key.name || key.value },
+ fix: fixer => fixer.replaceText(key, getQuotedKey(key))
+ });
+ }
+ }
+
+ /**
+ * Ensures that an object's keys are consistently quoted, optionally checks for redundancy of quotes
+ * @param {ASTNode} node Property AST node
+ * @param {boolean} checkQuotesRedundancy Whether to check quotes' redundancy
+ * @returns {void}
+ */
+ function checkConsistency(node, checkQuotesRedundancy) {
+ const quotedProps = [],
+ unquotedProps = [];
+ let keywordKeyName = null,
+ necessaryQuotes = false;
+
+ node.properties.forEach(property => {
+ const key = property.key;
+
+ if (!key || property.method || property.computed || property.shorthand) {
+ return;
+ }
+
+ if (key.type === "Literal" && typeof key.value === "string") {
+
+ quotedProps.push(property);
+
+ if (checkQuotesRedundancy) {
+ let tokens;
+
+ try {
+ tokens = espree.tokenize(key.value);
+ } catch (e) {
+ necessaryQuotes = true;
+ return;
+ }
+
+ necessaryQuotes = necessaryQuotes || !areQuotesRedundant(key.value, tokens) || KEYWORDS && isKeyword(tokens[0].value);
+ }
+ } else if (KEYWORDS && checkQuotesRedundancy && key.type === "Identifier" && isKeyword(key.name)) {
+ unquotedProps.push(property);
+ necessaryQuotes = true;
+ keywordKeyName = key.name;
+ } else {
+ unquotedProps.push(property);
+ }
+ });
+
+ if (checkQuotesRedundancy && quotedProps.length && !necessaryQuotes) {
+ quotedProps.forEach(property => {
+ context.report({
+ node: property,
+ message: "Properties shouldn't be quoted as all quotes are redundant.",
+ fix: fixer => fixer.replaceText(property.key, getUnquotedKey(property.key))
+ });
+ });
+ } else if (unquotedProps.length && keywordKeyName) {
+ unquotedProps.forEach(property => {
+ context.report({
+ node: property,
+ message: "Properties should be quoted as '{{property}}' is a reserved word.",
+ data: { property: keywordKeyName },
+ fix: fixer => fixer.replaceText(property.key, getQuotedKey(property.key))
+ });
+ });
+ } else if (quotedProps.length && unquotedProps.length) {
+ unquotedProps.forEach(property => {
+ context.report({
+ node: property,
+ message: "Inconsistently quoted property '{{key}}' found.",
+ data: { key: property.key.name || property.key.value },
+ fix: fixer => fixer.replaceText(property.key, getQuotedKey(property.key))
+ });
+ });
+ }
+ }
+
+ return {
+ Property(node) {
+ if (MODE === "always" || !MODE) {
+ checkOmittedQuotes(node);
+ }
+ if (MODE === "as-needed") {
+ checkUnnecessaryQuotes(node);
+ }
+ },
+ ObjectExpression(node) {
+ if (MODE === "consistent") {
+ checkConsistency(node, false);
+ }
+ if (MODE === "consistent-as-needed") {
+ checkConsistency(node, true);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/quotes.js b/tools/node_modules/eslint/lib/rules/quotes.js
new file mode 100644
index 0000000000..914762727b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/quotes.js
@@ -0,0 +1,296 @@
+/**
+ * @fileoverview A rule to choose between single and double quote marks
+ * @author Matt DuVall <http://www.mattduvall.com/>, Brandon Payton
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const QUOTE_SETTINGS = {
+ double: {
+ quote: "\"",
+ alternateQuote: "'",
+ description: "doublequote"
+ },
+ single: {
+ quote: "'",
+ alternateQuote: "\"",
+ description: "singlequote"
+ },
+ backtick: {
+ quote: "`",
+ alternateQuote: "\"",
+ description: "backtick"
+ }
+};
+
+// An unescaped newline is a newline preceded by an even number of backslashes.
+const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(astUtils.LINEBREAKS).join("")}]`);
+
+/**
+ * Switches quoting of javascript string between ' " and `
+ * escaping and unescaping as necessary.
+ * Only escaping of the minimal set of characters is changed.
+ * Note: escaping of newlines when switching from backtick to other quotes is not handled.
+ * @param {string} str - A string to convert.
+ * @returns {string} The string with changed quotes.
+ * @private
+ */
+QUOTE_SETTINGS.double.convert =
+QUOTE_SETTINGS.single.convert =
+QUOTE_SETTINGS.backtick.convert = function(str) {
+ const newQuote = this.quote;
+ const oldQuote = str[0];
+
+ if (newQuote === oldQuote) {
+ return str;
+ }
+ return newQuote + str.slice(1, -1).replace(/\\(\${|\r\n?|\n|.)|["'`]|\${|(\r\n?|\n)/g, (match, escaped, newline) => {
+ if (escaped === oldQuote || oldQuote === "`" && escaped === "${") {
+ return escaped; // unescape
+ }
+ if (match === newQuote || newQuote === "`" && match === "${") {
+ return `\\${match}`; // escape
+ }
+ if (newline && oldQuote === "`") {
+ return "\\n"; // escape newlines
+ }
+ return match;
+ }) + newQuote;
+};
+
+const AVOID_ESCAPE = "avoid-escape";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the consistent use of either backticks, double, or single quotes",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: [
+ {
+ enum: ["single", "double", "backtick"]
+ },
+ {
+ anyOf: [
+ {
+ enum: ["avoid-escape"]
+ },
+ {
+ type: "object",
+ properties: {
+ avoidEscape: {
+ type: "boolean"
+ },
+ allowTemplateLiterals: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+
+ const quoteOption = context.options[0],
+ settings = QUOTE_SETTINGS[quoteOption || "double"],
+ options = context.options[1],
+ allowTemplateLiterals = options && options.allowTemplateLiterals === true,
+ sourceCode = context.getSourceCode();
+ let avoidEscape = options && options.avoidEscape === true;
+
+ // deprecated
+ if (options === AVOID_ESCAPE) {
+ avoidEscape = true;
+ }
+
+ /**
+ * Determines if a given node is part of JSX syntax.
+ *
+ * This function returns `true` in the following cases:
+ *
+ * - `<div className="foo"></div>` ... If the literal is an attribute value, the parent of the literal is `JSXAttribute`.
+ * - `<div>foo</div>` ... If the literal is a text content, the parent of the literal is `JSXElement`.
+ *
+ * In particular, this function returns `false` in the following cases:
+ *
+ * - `<div className={"foo"}></div>`
+ * - `<div>{"foo"}</div>`
+ *
+ * In both cases, inside of the braces is handled as normal JavaScript.
+ * The braces are `JSXExpressionContainer` nodes.
+ *
+ * @param {ASTNode} node The Literal node to check.
+ * @returns {boolean} True if the node is a part of JSX, false if not.
+ * @private
+ */
+ function isJSXLiteral(node) {
+ return node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement";
+ }
+
+ /**
+ * Checks whether or not a given node is a directive.
+ * The directive is a `ExpressionStatement` which has only a string literal.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a directive.
+ * @private
+ */
+ function isDirective(node) {
+ return (
+ node.type === "ExpressionStatement" &&
+ node.expression.type === "Literal" &&
+ typeof node.expression.value === "string"
+ );
+ }
+
+ /**
+ * Checks whether or not a given node is a part of directive prologues.
+ * See also: http://www.ecma-international.org/ecma-262/6.0/#sec-directive-prologues-and-the-use-strict-directive
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a part of directive prologues.
+ * @private
+ */
+ function isPartOfDirectivePrologue(node) {
+ const block = node.parent.parent;
+
+ if (block.type !== "Program" && (block.type !== "BlockStatement" || !astUtils.isFunction(block.parent))) {
+ return false;
+ }
+
+ // Check the node is at a prologue.
+ for (let i = 0; i < block.body.length; ++i) {
+ const statement = block.body[i];
+
+ if (statement === node.parent) {
+ return true;
+ }
+ if (!isDirective(statement)) {
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether or not a given node is allowed as non backtick.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is allowed as non backtick.
+ * @private
+ */
+ function isAllowedAsNonBacktick(node) {
+ const parent = node.parent;
+
+ switch (parent.type) {
+
+ // Directive Prologues.
+ case "ExpressionStatement":
+ return isPartOfDirectivePrologue(node);
+
+ // LiteralPropertyName.
+ case "Property":
+ case "MethodDefinition":
+ return parent.key === node && !parent.computed;
+
+ // ModuleSpecifier.
+ case "ImportDeclaration":
+ case "ExportNamedDeclaration":
+ case "ExportAllDeclaration":
+ return parent.source === node;
+
+ // Others don't allow.
+ default:
+ return false;
+ }
+ }
+
+ return {
+
+ Literal(node) {
+ const val = node.value,
+ rawVal = node.raw;
+
+ if (settings && typeof val === "string") {
+ let isValid = (quoteOption === "backtick" && isAllowedAsNonBacktick(node)) ||
+ isJSXLiteral(node) ||
+ astUtils.isSurroundedBy(rawVal, settings.quote);
+
+ if (!isValid && avoidEscape) {
+ isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
+ }
+
+ if (!isValid) {
+ context.report({
+ node,
+ message: "Strings must use {{description}}.",
+ data: {
+ description: settings.description
+ },
+ fix(fixer) {
+ return fixer.replaceText(node, settings.convert(node.raw));
+ }
+ });
+ }
+ }
+ },
+
+ TemplateLiteral(node) {
+
+ // If backticks are expected or it's a tagged template, then this shouldn't throw an errors
+ if (
+ allowTemplateLiterals ||
+ quoteOption === "backtick" ||
+ node.parent.type === "TaggedTemplateExpression" && node === node.parent.quasi
+ ) {
+ return;
+ }
+
+ // A warning should be produced if the template literal only has one TemplateElement, and has no unescaped newlines.
+ const shouldWarn = node.quasis.length === 1 && !UNESCAPED_LINEBREAK_PATTERN.test(node.quasis[0].value.raw);
+
+ if (shouldWarn) {
+ context.report({
+ node,
+ message: "Strings must use {{description}}.",
+ data: {
+ description: settings.description
+ },
+ fix(fixer) {
+ if (isPartOfDirectivePrologue(node)) {
+
+ /*
+ * TemplateLiterals in a directive prologue aren't actually directives, but if they're
+ * in the directive prologue, then fixing them might turn them into directives and change
+ * the behavior of the code.
+ */
+ return null;
+ }
+ return fixer.replaceText(node, settings.convert(sourceCode.getText(node)));
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/radix.js b/tools/node_modules/eslint/lib/rules/radix.js
new file mode 100644
index 0000000000..0484c3bfb3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/radix.js
@@ -0,0 +1,171 @@
+/**
+ * @fileoverview Rule to flag use of parseInt without a radix argument
+ * @author James Allardice
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const MODE_ALWAYS = "always",
+ MODE_AS_NEEDED = "as-needed";
+
+/**
+ * Checks whether a given variable is shadowed or not.
+ *
+ * @param {eslint-scope.Variable} variable - A variable to check.
+ * @returns {boolean} `true` if the variable is shadowed.
+ */
+function isShadowed(variable) {
+ return variable.defs.length >= 1;
+}
+
+/**
+ * Checks whether a given node is a MemberExpression of `parseInt` method or not.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a MemberExpression of `parseInt`
+ * method.
+ */
+function isParseIntMethod(node) {
+ return (
+ node.type === "MemberExpression" &&
+ !node.computed &&
+ node.property.type === "Identifier" &&
+ node.property.name === "parseInt"
+ );
+}
+
+/**
+ * Checks whether a given node is a valid value of radix or not.
+ *
+ * The following values are invalid.
+ *
+ * - A literal except numbers.
+ * - undefined.
+ *
+ * @param {ASTNode} radix - A node of radix to check.
+ * @returns {boolean} `true` if the node is valid.
+ */
+function isValidRadix(radix) {
+ return !(
+ (radix.type === "Literal" && typeof radix.value !== "number") ||
+ (radix.type === "Identifier" && radix.name === "undefined")
+ );
+}
+
+/**
+ * Checks whether a given node is a default value of radix or not.
+ *
+ * @param {ASTNode} radix - A node of radix to check.
+ * @returns {boolean} `true` if the node is the literal node of `10`.
+ */
+function isDefaultRadix(radix) {
+ return radix.type === "Literal" && radix.value === 10;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce the consistent use of the radix argument when using `parseInt()`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["always", "as-needed"]
+ }
+ ]
+ },
+
+ create(context) {
+ const mode = context.options[0] || MODE_ALWAYS;
+
+ /**
+ * Checks the arguments of a given CallExpression node and reports it if it
+ * offends this rule.
+ *
+ * @param {ASTNode} node - A CallExpression node to check.
+ * @returns {void}
+ */
+ function checkArguments(node) {
+ const args = node.arguments;
+
+ switch (args.length) {
+ case 0:
+ context.report({
+ node,
+ message: "Missing parameters."
+ });
+ break;
+
+ case 1:
+ if (mode === MODE_ALWAYS) {
+ context.report({
+ node,
+ message: "Missing radix parameter."
+ });
+ }
+ break;
+
+ default:
+ if (mode === MODE_AS_NEEDED && isDefaultRadix(args[1])) {
+ context.report({
+ node,
+ message: "Redundant radix parameter."
+ });
+ } else if (!isValidRadix(args[1])) {
+ context.report({
+ node,
+ message: "Invalid radix parameter."
+ });
+ }
+ break;
+ }
+ }
+
+ return {
+ "Program:exit"() {
+ const scope = context.getScope();
+ let variable;
+
+ // Check `parseInt()`
+ variable = astUtils.getVariableByName(scope, "parseInt");
+ if (!isShadowed(variable)) {
+ variable.references.forEach(reference => {
+ const node = reference.identifier;
+
+ if (astUtils.isCallee(node)) {
+ checkArguments(node.parent);
+ }
+ });
+ }
+
+ // Check `Number.parseInt()`
+ variable = astUtils.getVariableByName(scope, "Number");
+ if (!isShadowed(variable)) {
+ variable.references.forEach(reference => {
+ const node = reference.identifier.parent;
+
+ if (isParseIntMethod(node) && astUtils.isCallee(node)) {
+ checkArguments(node.parent);
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/require-await.js b/tools/node_modules/eslint/lib/rules/require-await.js
new file mode 100644
index 0000000000..a5698ae058
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/require-await.js
@@ -0,0 +1,95 @@
+/**
+ * @fileoverview Rule to disallow async functions which have no `await` expression.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Capitalize the 1st letter of the given text.
+ *
+ * @param {string} text - The text to capitalize.
+ * @returns {string} The text that the 1st letter was capitalized.
+ */
+function capitalizeFirstLetter(text) {
+ return text[0].toUpperCase() + text.slice(1);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow async functions which have no `await` expression",
+ category: "Best Practices",
+ recommended: false
+ },
+ schema: []
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ let scopeInfo = null;
+
+ /**
+ * Push the scope info object to the stack.
+ *
+ * @returns {void}
+ */
+ function enterFunction() {
+ scopeInfo = {
+ upper: scopeInfo,
+ hasAwait: false
+ };
+ }
+
+ /**
+ * Pop the top scope info object from the stack.
+ * Also, it reports the function if needed.
+ *
+ * @param {ASTNode} node - The node to report.
+ * @returns {void}
+ */
+ function exitFunction(node) {
+ if (node.async && !scopeInfo.hasAwait && !astUtils.isEmptyFunction(node)) {
+ context.report({
+ node,
+ loc: astUtils.getFunctionHeadLoc(node, sourceCode),
+ message: "{{name}} has no 'await' expression.",
+ data: {
+ name: capitalizeFirstLetter(
+ astUtils.getFunctionNameWithKind(node)
+ )
+ }
+ });
+ }
+
+ scopeInfo = scopeInfo.upper;
+ }
+
+ return {
+ FunctionDeclaration: enterFunction,
+ FunctionExpression: enterFunction,
+ ArrowFunctionExpression: enterFunction,
+ "FunctionDeclaration:exit": exitFunction,
+ "FunctionExpression:exit": exitFunction,
+ "ArrowFunctionExpression:exit": exitFunction,
+
+ AwaitExpression() {
+ scopeInfo.hasAwait = true;
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/require-jsdoc.js b/tools/node_modules/eslint/lib/rules/require-jsdoc.js
new file mode 100644
index 0000000000..a02ee3659c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/require-jsdoc.js
@@ -0,0 +1,105 @@
+/**
+ * @fileoverview Rule to check for jsdoc presence.
+ * @author Gyandeep Singh
+ */
+"use strict";
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require JSDoc comments",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ require: {
+ type: "object",
+ properties: {
+ ClassDeclaration: {
+ type: "boolean"
+ },
+ MethodDefinition: {
+ type: "boolean"
+ },
+ FunctionDeclaration: {
+ type: "boolean"
+ },
+ ArrowFunctionExpression: {
+ type: "boolean"
+ },
+ FunctionExpression: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const source = context.getSourceCode();
+ const DEFAULT_OPTIONS = {
+ FunctionDeclaration: true,
+ MethodDefinition: false,
+ ClassDeclaration: false,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false
+ };
+ const options = Object.assign(DEFAULT_OPTIONS, context.options[0] && context.options[0].require || {});
+
+ /**
+ * Report the error message
+ * @param {ASTNode} node node to report
+ * @returns {void}
+ */
+ function report(node) {
+ context.report({ node, message: "Missing JSDoc comment." });
+ }
+
+ /**
+ * Check if the jsdoc comment is present or not.
+ * @param {ASTNode} node node to examine
+ * @returns {void}
+ */
+ function checkJsDoc(node) {
+ const jsdocComment = source.getJSDocComment(node);
+
+ if (!jsdocComment) {
+ report(node);
+ }
+ }
+
+ return {
+ FunctionDeclaration(node) {
+ if (options.FunctionDeclaration) {
+ checkJsDoc(node);
+ }
+ },
+ FunctionExpression(node) {
+ if (
+ (options.MethodDefinition && node.parent.type === "MethodDefinition") ||
+ (options.FunctionExpression && (node.parent.type === "VariableDeclarator" || (node.parent.type === "Property" && node === node.parent.value)))
+ ) {
+ checkJsDoc(node);
+ }
+ },
+ ClassDeclaration(node) {
+ if (options.ClassDeclaration) {
+ checkJsDoc(node);
+ }
+ },
+ ArrowFunctionExpression(node) {
+ if (options.ArrowFunctionExpression && node.parent.type === "VariableDeclarator") {
+ checkJsDoc(node);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/require-yield.js b/tools/node_modules/eslint/lib/rules/require-yield.js
new file mode 100644
index 0000000000..5cc2944bc6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/require-yield.js
@@ -0,0 +1,71 @@
+/**
+ * @fileoverview Rule to flag the generator functions that does not have yield.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require generator functions to contain `yield`",
+ category: "ECMAScript 6",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const stack = [];
+
+ /**
+ * If the node is a generator function, start counting `yield` keywords.
+ * @param {Node} node - A function node to check.
+ * @returns {void}
+ */
+ function beginChecking(node) {
+ if (node.generator) {
+ stack.push(0);
+ }
+ }
+
+ /**
+ * If the node is a generator function, end counting `yield` keywords, then
+ * reports result.
+ * @param {Node} node - A function node to check.
+ * @returns {void}
+ */
+ function endChecking(node) {
+ if (!node.generator) {
+ return;
+ }
+
+ const countYield = stack.pop();
+
+ if (countYield === 0 && node.body.body.length > 0) {
+ context.report({ node, message: "This generator function does not have 'yield'." });
+ }
+ }
+
+ return {
+ FunctionDeclaration: beginChecking,
+ "FunctionDeclaration:exit": endChecking,
+ FunctionExpression: beginChecking,
+ "FunctionExpression:exit": endChecking,
+
+ // Increases the count of `yield` keyword.
+ YieldExpression() {
+
+ /* istanbul ignore else */
+ if (stack.length > 0) {
+ stack[stack.length - 1] += 1;
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/rest-spread-spacing.js b/tools/node_modules/eslint/lib/rules/rest-spread-spacing.js
new file mode 100644
index 0000000000..91770eca74
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/rest-spread-spacing.js
@@ -0,0 +1,107 @@
+/**
+ * @fileoverview Enforce spacing between rest and spread operators and their expressions.
+ * @author Kai Cataldo
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce spacing between rest and spread operators and their expressions",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+ fixable: "whitespace",
+ schema: [
+ {
+ enum: ["always", "never"]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode(),
+ alwaysSpace = context.options[0] === "always";
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Checks whitespace between rest/spread operators and their expressions
+ * @param {ASTNode} node - The node to check
+ * @returns {void}
+ */
+ function checkWhiteSpace(node) {
+ const operator = sourceCode.getFirstToken(node),
+ nextToken = sourceCode.getTokenAfter(operator),
+ hasWhitespace = sourceCode.isSpaceBetweenTokens(operator, nextToken);
+ let type;
+
+ switch (node.type) {
+ case "SpreadElement":
+ type = "spread";
+ break;
+ case "RestElement":
+ type = "rest";
+ break;
+ case "ExperimentalSpreadProperty":
+ type = "spread property";
+ break;
+ case "ExperimentalRestProperty":
+ type = "rest property";
+ break;
+ default:
+ return;
+ }
+
+ if (alwaysSpace && !hasWhitespace) {
+ context.report({
+ node,
+ loc: {
+ line: operator.loc.end.line,
+ column: operator.loc.end.column
+ },
+ message: "Expected whitespace after {{type}} operator.",
+ data: {
+ type
+ },
+ fix(fixer) {
+ return fixer.replaceTextRange([operator.range[1], nextToken.range[0]], " ");
+ }
+ });
+ } else if (!alwaysSpace && hasWhitespace) {
+ context.report({
+ node,
+ loc: {
+ line: operator.loc.end.line,
+ column: operator.loc.end.column
+ },
+ message: "Unexpected whitespace after {{type}} operator.",
+ data: {
+ type
+ },
+ fix(fixer) {
+ return fixer.removeRange([operator.range[1], nextToken.range[0]]);
+ }
+ });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ SpreadElement: checkWhiteSpace,
+ RestElement: checkWhiteSpace,
+ ExperimentalSpreadProperty: checkWhiteSpace,
+ ExperimentalRestProperty: checkWhiteSpace
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/semi-spacing.js b/tools/node_modules/eslint/lib/rules/semi-spacing.js
new file mode 100644
index 0000000000..fd300e4a37
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/semi-spacing.js
@@ -0,0 +1,211 @@
+/**
+ * @fileoverview Validates spacing before and after semicolon
+ * @author Mathias Schreck
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before and after semicolons",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ before: {
+ type: "boolean"
+ },
+ after: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const config = context.options[0],
+ sourceCode = context.getSourceCode();
+ let requireSpaceBefore = false,
+ requireSpaceAfter = true;
+
+ if (typeof config === "object") {
+ if (config.hasOwnProperty("before")) {
+ requireSpaceBefore = config.before;
+ }
+ if (config.hasOwnProperty("after")) {
+ requireSpaceAfter = config.after;
+ }
+ }
+
+ /**
+ * Checks if a given token has leading whitespace.
+ * @param {Object} token The token to check.
+ * @returns {boolean} True if the given token has leading space, false if not.
+ */
+ function hasLeadingSpace(token) {
+ const tokenBefore = sourceCode.getTokenBefore(token);
+
+ return tokenBefore && astUtils.isTokenOnSameLine(tokenBefore, token) && sourceCode.isSpaceBetweenTokens(tokenBefore, token);
+ }
+
+ /**
+ * Checks if a given token has trailing whitespace.
+ * @param {Object} token The token to check.
+ * @returns {boolean} True if the given token has trailing space, false if not.
+ */
+ function hasTrailingSpace(token) {
+ const tokenAfter = sourceCode.getTokenAfter(token);
+
+ return tokenAfter && astUtils.isTokenOnSameLine(token, tokenAfter) && sourceCode.isSpaceBetweenTokens(token, tokenAfter);
+ }
+
+ /**
+ * Checks if the given token is the last token in its line.
+ * @param {Token} token The token to check.
+ * @returns {boolean} Whether or not the token is the last in its line.
+ */
+ function isLastTokenInCurrentLine(token) {
+ const tokenAfter = sourceCode.getTokenAfter(token);
+
+ return !(tokenAfter && astUtils.isTokenOnSameLine(token, tokenAfter));
+ }
+
+ /**
+ * Checks if the given token is the first token in its line
+ * @param {Token} token The token to check.
+ * @returns {boolean} Whether or not the token is the first in its line.
+ */
+ function isFirstTokenInCurrentLine(token) {
+ const tokenBefore = sourceCode.getTokenBefore(token);
+
+ return !(tokenBefore && astUtils.isTokenOnSameLine(token, tokenBefore));
+ }
+
+ /**
+ * Checks if the next token of a given token is a closing parenthesis.
+ * @param {Token} token The token to check.
+ * @returns {boolean} Whether or not the next token of a given token is a closing parenthesis.
+ */
+ function isBeforeClosingParen(token) {
+ const nextToken = sourceCode.getTokenAfter(token);
+
+ return (nextToken && astUtils.isClosingBraceToken(nextToken) || astUtils.isClosingParenToken(nextToken));
+ }
+
+ /**
+ * Reports if the given token has invalid spacing.
+ * @param {Token} token The semicolon token to check.
+ * @param {ASTNode} node The corresponding node of the token.
+ * @returns {void}
+ */
+ function checkSemicolonSpacing(token, node) {
+ if (astUtils.isSemicolonToken(token)) {
+ const location = token.loc.start;
+
+ if (hasLeadingSpace(token)) {
+ if (!requireSpaceBefore) {
+ context.report({
+ node,
+ loc: location,
+ message: "Unexpected whitespace before semicolon.",
+ fix(fixer) {
+ const tokenBefore = sourceCode.getTokenBefore(token);
+
+ return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
+ }
+ });
+ }
+ } else {
+ if (requireSpaceBefore) {
+ context.report({
+ node,
+ loc: location,
+ message: "Missing whitespace before semicolon.",
+ fix(fixer) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ });
+ }
+ }
+
+ if (!isFirstTokenInCurrentLine(token) && !isLastTokenInCurrentLine(token) && !isBeforeClosingParen(token)) {
+ if (hasTrailingSpace(token)) {
+ if (!requireSpaceAfter) {
+ context.report({
+ node,
+ loc: location,
+ message: "Unexpected whitespace after semicolon.",
+ fix(fixer) {
+ const tokenAfter = sourceCode.getTokenAfter(token);
+
+ return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
+ }
+ });
+ }
+ } else {
+ if (requireSpaceAfter) {
+ context.report({
+ node,
+ loc: location,
+ message: "Missing whitespace after semicolon.",
+ fix(fixer) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks the spacing of the semicolon with the assumption that the last token is the semicolon.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkNode(node) {
+ const token = sourceCode.getLastToken(node);
+
+ checkSemicolonSpacing(token, node);
+ }
+
+ return {
+ VariableDeclaration: checkNode,
+ ExpressionStatement: checkNode,
+ BreakStatement: checkNode,
+ ContinueStatement: checkNode,
+ DebuggerStatement: checkNode,
+ ReturnStatement: checkNode,
+ ThrowStatement: checkNode,
+ ImportDeclaration: checkNode,
+ ExportNamedDeclaration: checkNode,
+ ExportAllDeclaration: checkNode,
+ ExportDefaultDeclaration: checkNode,
+ ForStatement(node) {
+ if (node.init) {
+ checkSemicolonSpacing(sourceCode.getTokenAfter(node.init), node);
+ }
+
+ if (node.test) {
+ checkSemicolonSpacing(sourceCode.getTokenAfter(node.test), node);
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/semi-style.js b/tools/node_modules/eslint/lib/rules/semi-style.js
new file mode 100644
index 0000000000..41fb39246c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/semi-style.js
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview Rule to enforce location of semicolons.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const SELECTOR = `:matches(${
+ [
+ "BreakStatement", "ContinueStatement", "DebuggerStatement",
+ "DoWhileStatement", "ExportAllDeclaration",
+ "ExportDefaultDeclaration", "ExportNamedDeclaration",
+ "ExpressionStatement", "ImportDeclaration", "ReturnStatement",
+ "ThrowStatement", "VariableDeclaration"
+ ].join(",")
+})`;
+
+/**
+ * Get the child node list of a given node.
+ * This returns `Program#body`, `BlockStatement#body`, or `SwitchCase#consequent`.
+ * This is used to check whether a node is the first/last child.
+ * @param {Node} node A node to get child node list.
+ * @returns {Node[]|null} The child node list.
+ */
+function getChildren(node) {
+ const t = node.type;
+
+ if (t === "BlockStatement" || t === "Program") {
+ return node.body;
+ }
+ if (t === "SwitchCase") {
+ return node.consequent;
+ }
+ return null;
+}
+
+/**
+ * Check whether a given node is the last statement in the parent block.
+ * @param {Node} node A node to check.
+ * @returns {boolean} `true` if the node is the last statement in the parent block.
+ */
+function isLastChild(node) {
+ const t = node.parent.type;
+
+ if (t === "IfStatement" && node.parent.consequent === node && node.parent.alternate) { // before `else` keyword.
+ return true;
+ }
+ if (t === "DoWhileStatement") { // before `while` keyword.
+ return true;
+ }
+ const nodeList = getChildren(node.parent);
+
+ return nodeList !== null && nodeList[nodeList.length - 1] === node; // before `}` or etc.
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce location of semicolons",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: [{ enum: ["last", "first"] }],
+ fixable: "whitespace"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const option = context.options[0] || "last";
+
+ /**
+ * Check the given semicolon token.
+ * @param {Token} semiToken The semicolon token to check.
+ * @param {"first"|"last"} expected The expected location to check.
+ * @returns {void}
+ */
+ function check(semiToken, expected) {
+ const prevToken = sourceCode.getTokenBefore(semiToken);
+ const nextToken = sourceCode.getTokenAfter(semiToken);
+ const prevIsSameLine = !prevToken || astUtils.isTokenOnSameLine(prevToken, semiToken);
+ const nextIsSameLine = !nextToken || astUtils.isTokenOnSameLine(semiToken, nextToken);
+
+ if ((expected === "last" && !prevIsSameLine) || (expected === "first" && !nextIsSameLine)) {
+ context.report({
+ loc: semiToken.loc,
+ message: "Expected this semicolon to be at {{pos}}.",
+ data: {
+ pos: (expected === "last")
+ ? "the end of the previous line"
+ : "the beginning of the next line"
+ },
+ fix(fixer) {
+ if (prevToken && nextToken && sourceCode.commentsExistBetween(prevToken, nextToken)) {
+ return null;
+ }
+
+ const start = prevToken ? prevToken.range[1] : semiToken.range[0];
+ const end = nextToken ? nextToken.range[0] : semiToken.range[1];
+ const text = (expected === "last") ? ";\n" : "\n;";
+
+ return fixer.replaceTextRange([start, end], text);
+ }
+ });
+ }
+ }
+
+ return {
+ [SELECTOR](node) {
+ if (option === "first" && isLastChild(node)) {
+ return;
+ }
+
+ const lastToken = sourceCode.getLastToken(node);
+
+ if (astUtils.isSemicolonToken(lastToken)) {
+ check(lastToken, option);
+ }
+ },
+
+ ForStatement(node) {
+ const firstSemi = node.init && sourceCode.getTokenAfter(node.init, astUtils.isSemicolonToken);
+ const secondSemi = node.test && sourceCode.getTokenAfter(node.test, astUtils.isSemicolonToken);
+
+ if (firstSemi) {
+ check(firstSemi, "last");
+ }
+ if (secondSemi) {
+ check(secondSemi, "last");
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/semi.js b/tools/node_modules/eslint/lib/rules/semi.js
new file mode 100644
index 0000000000..78b6966dea
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/semi.js
@@ -0,0 +1,325 @@
+/**
+ * @fileoverview Rule to flag missing semicolons.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const FixTracker = require("../util/fix-tracker");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow semicolons instead of ASI",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "code",
+
+ schema: {
+ anyOf: [
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["never"]
+ },
+ {
+ type: "object",
+ properties: {
+ beforeStatementContinuationChars: {
+ enum: ["always", "any", "never"]
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ },
+ {
+ type: "array",
+ items: [
+ {
+ enum: ["always"]
+ },
+ {
+ type: "object",
+ properties: {
+ omitLastInOneLineBlock: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ],
+ minItems: 0,
+ maxItems: 2
+ }
+ ]
+ }
+ },
+
+ create(context) {
+
+ const OPT_OUT_PATTERN = /^[-[(/+`]/; // One of [(/+-`
+ const options = context.options[1];
+ const never = context.options[0] === "never";
+ const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
+ const beforeStatementContinuationChars = (options && options.beforeStatementContinuationChars) || "any";
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports a semicolon error with appropriate location and message.
+ * @param {ASTNode} node The node with an extra or missing semicolon.
+ * @param {boolean} missing True if the semicolon is missing.
+ * @returns {void}
+ */
+ function report(node, missing) {
+ const lastToken = sourceCode.getLastToken(node);
+ let message,
+ fix,
+ loc = lastToken.loc;
+
+ if (!missing) {
+ message = "Missing semicolon.";
+ loc = loc.end;
+ fix = function(fixer) {
+ return fixer.insertTextAfter(lastToken, ";");
+ };
+ } else {
+ message = "Extra semicolon.";
+ loc = loc.start;
+ fix = function(fixer) {
+
+ /*
+ * Expand the replacement range to include the surrounding
+ * tokens to avoid conflicting with no-extra-semi.
+ * https://github.com/eslint/eslint/issues/7928
+ */
+ return new FixTracker(fixer, sourceCode)
+ .retainSurroundingTokens(lastToken)
+ .remove(lastToken);
+ };
+ }
+
+ context.report({
+ node,
+ loc,
+ message,
+ fix
+ });
+
+ }
+
+ /**
+ * Check whether a given semicolon token is redandant.
+ * @param {Token} semiToken A semicolon token to check.
+ * @returns {boolean} `true` if the next token is `;` or `}`.
+ */
+ function isRedundantSemi(semiToken) {
+ const nextToken = sourceCode.getTokenAfter(semiToken);
+
+ return (
+ !nextToken ||
+ astUtils.isClosingBraceToken(nextToken) ||
+ astUtils.isSemicolonToken(nextToken)
+ );
+ }
+
+ /**
+ * Check whether a given token is the closing brace of an arrow function.
+ * @param {Token} lastToken A token to check.
+ * @returns {boolean} `true` if the token is the closing brace of an arrow function.
+ */
+ function isEndOfArrowBlock(lastToken) {
+ if (!astUtils.isClosingBraceToken(lastToken)) {
+ return false;
+ }
+ const node = sourceCode.getNodeByRangeIndex(lastToken.range[0]);
+
+ return (
+ node.type === "BlockStatement" &&
+ node.parent.type === "ArrowFunctionExpression"
+ );
+ }
+
+ /**
+ * Check whether a given node is on the same line with the next token.
+ * @param {Node} node A statement node to check.
+ * @returns {boolean} `true` if the node is on the same line with the next token.
+ */
+ function isOnSameLineWithNextToken(node) {
+ const prevToken = sourceCode.getLastToken(node, 1);
+ const nextToken = sourceCode.getTokenAfter(node);
+
+ return !!nextToken && astUtils.isTokenOnSameLine(prevToken, nextToken);
+ }
+
+ /**
+ * Check whether a given node can connect the next line if the next line is unreliable.
+ * @param {Node} node A statement node to check.
+ * @returns {boolean} `true` if the node can connect the next line.
+ */
+ function maybeAsiHazardAfter(node) {
+ const t = node.type;
+
+ if (t === "DoWhileStatement" ||
+ t === "BreakStatement" ||
+ t === "ContinueStatement" ||
+ t === "DebuggerStatement" ||
+ t === "ImportDeclaration" ||
+ t === "ExportAllDeclaration"
+ ) {
+ return false;
+ }
+ if (t === "ReturnStatement") {
+ return Boolean(node.argument);
+ }
+ if (t === "ExportNamedDeclaration") {
+ return Boolean(node.declaration);
+ }
+ if (isEndOfArrowBlock(sourceCode.getLastToken(node, 1))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check whether a given token can connect the previous statement.
+ * @param {Token} token A token to check.
+ * @returns {boolean} `true` if the token is one of `[`, `(`, `/`, `+`, `-`, ```, `++`, and `--`.
+ */
+ function maybeAsiHazardBefore(token) {
+ return (
+ Boolean(token) &&
+ OPT_OUT_PATTERN.test(token.value) &&
+ token.value !== "++" &&
+ token.value !== "--"
+ );
+ }
+
+ /**
+ * Check if the semicolon of a given node is unnecessary, only true if:
+ * - next token is a valid statement divider (`;` or `}`).
+ * - next token is on a new line and the node is not connectable to the new line.
+ * @param {Node} node A statement node to check.
+ * @returns {boolean} whether the semicolon is unnecessary.
+ */
+ function canRemoveSemicolon(node) {
+ if (isRedundantSemi(sourceCode.getLastToken(node))) {
+ return true; // `;;` or `;}`
+ }
+ if (isOnSameLineWithNextToken(node)) {
+ return false; // One liner.
+ }
+ if (beforeStatementContinuationChars === "never" && !maybeAsiHazardAfter(node)) {
+ return true; // ASI works. This statement doesn't connect to the next.
+ }
+ if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
+ return true; // ASI works. The next token doesn't connect to this statement.
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks a node to see if it's in a one-liner block statement.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} whether the node is in a one-liner block statement.
+ */
+ function isOneLinerBlock(node) {
+ const parent = node.parent;
+ const nextToken = sourceCode.getTokenAfter(node);
+
+ if (!nextToken || nextToken.value !== "}") {
+ return false;
+ }
+ return (
+ !!parent &&
+ parent.type === "BlockStatement" &&
+ parent.loc.start.line === parent.loc.end.line
+ );
+ }
+
+ /**
+ * Checks a node to see if it's followed by a semicolon.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkForSemicolon(node) {
+ const isSemi = astUtils.isSemicolonToken(sourceCode.getLastToken(node));
+
+ if (never) {
+ if (isSemi && canRemoveSemicolon(node)) {
+ report(node, true);
+ } else if (!isSemi && beforeStatementContinuationChars === "always" && maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
+ report(node);
+ }
+ } else {
+ const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
+
+ if (isSemi && oneLinerBlock) {
+ report(node, true);
+ } else if (!isSemi && !oneLinerBlock) {
+ report(node);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if there's a semicolon after a variable declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkForSemicolonForVariableDeclaration(node) {
+ const parent = node.parent;
+
+ if ((parent.type !== "ForStatement" || parent.init !== node) &&
+ (!/^For(?:In|Of)Statement/.test(parent.type) || parent.left !== node)
+ ) {
+ checkForSemicolon(node);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ VariableDeclaration: checkForSemicolonForVariableDeclaration,
+ ExpressionStatement: checkForSemicolon,
+ ReturnStatement: checkForSemicolon,
+ ThrowStatement: checkForSemicolon,
+ DoWhileStatement: checkForSemicolon,
+ DebuggerStatement: checkForSemicolon,
+ BreakStatement: checkForSemicolon,
+ ContinueStatement: checkForSemicolon,
+ ImportDeclaration: checkForSemicolon,
+ ExportAllDeclaration: checkForSemicolon,
+ ExportNamedDeclaration(node) {
+ if (!node.declaration) {
+ checkForSemicolon(node);
+ }
+ },
+ ExportDefaultDeclaration(node) {
+ if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) {
+ checkForSemicolon(node);
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/sort-imports.js b/tools/node_modules/eslint/lib/rules/sort-imports.js
new file mode 100644
index 0000000000..2bd415e974
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/sort-imports.js
@@ -0,0 +1,196 @@
+/**
+ * @fileoverview Rule to require sorting of import declarations
+ * @author Christian Schuller
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce sorted import declarations within modules",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ ignoreCase: {
+ type: "boolean"
+ },
+ memberSyntaxSortOrder: {
+ type: "array",
+ items: {
+ enum: ["none", "all", "multiple", "single"]
+ },
+ uniqueItems: true,
+ minItems: 4,
+ maxItems: 4
+ },
+ ignoreMemberSort: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const configuration = context.options[0] || {},
+ ignoreCase = configuration.ignoreCase || false,
+ ignoreMemberSort = configuration.ignoreMemberSort || false,
+ memberSyntaxSortOrder = configuration.memberSyntaxSortOrder || ["none", "all", "multiple", "single"],
+ sourceCode = context.getSourceCode();
+ let previousDeclaration = null;
+
+ /**
+ * Gets the used member syntax style.
+ *
+ * import "my-module.js" --> none
+ * import * as myModule from "my-module.js" --> all
+ * import {myMember} from "my-module.js" --> single
+ * import {foo, bar} from "my-module.js" --> multiple
+ *
+ * @param {ASTNode} node - the ImportDeclaration node.
+ * @returns {string} used member parameter style, ["all", "multiple", "single"]
+ */
+ function usedMemberSyntax(node) {
+ if (node.specifiers.length === 0) {
+ return "none";
+ }
+ if (node.specifiers[0].type === "ImportNamespaceSpecifier") {
+ return "all";
+ }
+ if (node.specifiers.length === 1) {
+ return "single";
+ }
+ return "multiple";
+
+ }
+
+ /**
+ * Gets the group by member parameter index for given declaration.
+ * @param {ASTNode} node - the ImportDeclaration node.
+ * @returns {number} the declaration group by member index.
+ */
+ function getMemberParameterGroupIndex(node) {
+ return memberSyntaxSortOrder.indexOf(usedMemberSyntax(node));
+ }
+
+ /**
+ * Gets the local name of the first imported module.
+ * @param {ASTNode} node - the ImportDeclaration node.
+ * @returns {?string} the local name of the first imported module.
+ */
+ function getFirstLocalMemberName(node) {
+ if (node.specifiers[0]) {
+ return node.specifiers[0].local.name;
+ }
+ return null;
+
+ }
+
+ return {
+ ImportDeclaration(node) {
+ if (previousDeclaration) {
+ const currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
+ previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
+ let currentLocalMemberName = getFirstLocalMemberName(node),
+ previousLocalMemberName = getFirstLocalMemberName(previousDeclaration);
+
+ if (ignoreCase) {
+ previousLocalMemberName = previousLocalMemberName && previousLocalMemberName.toLowerCase();
+ currentLocalMemberName = currentLocalMemberName && currentLocalMemberName.toLowerCase();
+ }
+
+ /*
+ * When the current declaration uses a different member syntax,
+ * then check if the ordering is correct.
+ * Otherwise, make a default string compare (like rule sort-vars to be consistent) of the first used local member name.
+ */
+ if (currentMemberSyntaxGroupIndex !== previousMemberSyntaxGroupIndex) {
+ if (currentMemberSyntaxGroupIndex < previousMemberSyntaxGroupIndex) {
+ context.report({
+ node,
+ message: "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
+ data: {
+ syntaxA: memberSyntaxSortOrder[currentMemberSyntaxGroupIndex],
+ syntaxB: memberSyntaxSortOrder[previousMemberSyntaxGroupIndex]
+ }
+ });
+ }
+ } else {
+ if (previousLocalMemberName &&
+ currentLocalMemberName &&
+ currentLocalMemberName < previousLocalMemberName
+ ) {
+ context.report({
+ node,
+ message: "Imports should be sorted alphabetically."
+ });
+ }
+ }
+ }
+
+ if (!ignoreMemberSort) {
+ const importSpecifiers = node.specifiers.filter(specifier => specifier.type === "ImportSpecifier");
+ const getSortableName = ignoreCase ? specifier => specifier.local.name.toLowerCase() : specifier => specifier.local.name;
+ const firstUnsortedIndex = importSpecifiers.map(getSortableName).findIndex((name, index, array) => array[index - 1] > name);
+
+ if (firstUnsortedIndex !== -1) {
+ context.report({
+ node: importSpecifiers[firstUnsortedIndex],
+ message: "Member '{{memberName}}' of the import declaration should be sorted alphabetically.",
+ data: { memberName: importSpecifiers[firstUnsortedIndex].local.name },
+ fix(fixer) {
+ if (importSpecifiers.some(specifier =>
+ sourceCode.getCommentsBefore(specifier).length || sourceCode.getCommentsAfter(specifier).length)) {
+
+ // If there are comments in the ImportSpecifier list, don't rearrange the specifiers.
+ return null;
+ }
+
+ return fixer.replaceTextRange(
+ [importSpecifiers[0].range[0], importSpecifiers[importSpecifiers.length - 1].range[1]],
+ importSpecifiers
+
+ // Clone the importSpecifiers array to avoid mutating it
+ .slice()
+
+ // Sort the array into the desired order
+ .sort((specifierA, specifierB) => {
+ const aName = getSortableName(specifierA);
+ const bName = getSortableName(specifierB);
+
+ return aName > bName ? 1 : -1;
+ })
+
+ // Build a string out of the sorted list of import specifiers and the text between the originals
+ .reduce((sourceText, specifier, index) => {
+ const textAfterSpecifier = index === importSpecifiers.length - 1
+ ? ""
+ : sourceCode.getText().slice(importSpecifiers[index].range[1], importSpecifiers[index + 1].range[0]);
+
+ return sourceText + sourceCode.getText(specifier) + textAfterSpecifier;
+ }, "")
+ );
+ }
+ });
+ }
+ }
+
+ previousDeclaration = node;
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/sort-keys.js b/tools/node_modules/eslint/lib/rules/sort-keys.js
new file mode 100644
index 0000000000..8821f62943
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/sort-keys.js
@@ -0,0 +1,157 @@
+/**
+ * @fileoverview Rule to require object keys to be sorted
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils"),
+ naturalCompare = require("natural-compare");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets the property name of the given `Property` node.
+ *
+ * - If the property's key is an `Identifier` node, this returns the key's name
+ * whether it's a computed property or not.
+ * - If the property has a static name, this returns the static name.
+ * - Otherwise, this returns null.
+ *
+ * @param {ASTNode} node - The `Property` node to get.
+ * @returns {string|null} The property name or null.
+ * @private
+ */
+function getPropertyName(node) {
+ return astUtils.getStaticPropertyName(node) || node.key.name || null;
+}
+
+/**
+ * Functions which check that the given 2 names are in specific order.
+ *
+ * Postfix `I` is meant insensitive.
+ * Postfix `N` is meant natual.
+ *
+ * @private
+ */
+const isValidOrders = {
+ asc(a, b) {
+ return a <= b;
+ },
+ ascI(a, b) {
+ return a.toLowerCase() <= b.toLowerCase();
+ },
+ ascN(a, b) {
+ return naturalCompare(a, b) <= 0;
+ },
+ ascIN(a, b) {
+ return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0;
+ },
+ desc(a, b) {
+ return isValidOrders.asc(b, a);
+ },
+ descI(a, b) {
+ return isValidOrders.ascI(b, a);
+ },
+ descN(a, b) {
+ return isValidOrders.ascN(b, a);
+ },
+ descIN(a, b) {
+ return isValidOrders.ascIN(b, a);
+ }
+};
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require object keys to be sorted",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: [
+ {
+ enum: ["asc", "desc"]
+ },
+ {
+ type: "object",
+ properties: {
+ caseSensitive: {
+ type: "boolean"
+ },
+ natural: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ // Parse options.
+ const order = context.options[0] || "asc";
+ const options = context.options[1];
+ const insensitive = (options && options.caseSensitive) === false;
+ const natual = Boolean(options && options.natural);
+ const isValidOrder = isValidOrders[
+ order + (insensitive ? "I" : "") + (natual ? "N" : "")
+ ];
+
+ // The stack to save the previous property's name for each object literals.
+ let stack = null;
+
+ return {
+ ObjectExpression() {
+ stack = {
+ upper: stack,
+ prevName: null
+ };
+ },
+
+ "ObjectExpression:exit"() {
+ stack = stack.upper;
+ },
+
+ Property(node) {
+ if (node.parent.type === "ObjectPattern") {
+ return;
+ }
+
+ const prevName = stack.prevName;
+ const thisName = getPropertyName(node);
+
+ stack.prevName = thisName || prevName;
+
+ if (!prevName || !thisName) {
+ return;
+ }
+
+ if (!isValidOrder(prevName, thisName)) {
+ context.report({
+ node,
+ loc: node.key.loc,
+ message: "Expected object keys to be in {{natual}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'.",
+ data: {
+ thisName,
+ prevName,
+ order,
+ insensitive: insensitive ? "insensitive " : "",
+ natual: natual ? "natural " : ""
+ }
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/sort-vars.js b/tools/node_modules/eslint/lib/rules/sort-vars.js
new file mode 100644
index 0000000000..c77cdf8620
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/sort-vars.js
@@ -0,0 +1,96 @@
+/**
+ * @fileoverview Rule to require sorting of variables within a single Variable Declaration block
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require variables within the same declaration block to be sorted",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ ignoreCase: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const configuration = context.options[0] || {},
+ ignoreCase = configuration.ignoreCase || false,
+ sourceCode = context.getSourceCode();
+
+ return {
+ VariableDeclaration(node) {
+ const idDeclarations = node.declarations.filter(decl => decl.id.type === "Identifier");
+ const getSortableName = ignoreCase ? decl => decl.id.name.toLowerCase() : decl => decl.id.name;
+ const unfixable = idDeclarations.some(decl => decl.init !== null && decl.init.type !== "Literal");
+ let fixed = false;
+
+ idDeclarations.slice(1).reduce((memo, decl) => {
+ const lastVariableName = getSortableName(memo),
+ currentVariableName = getSortableName(decl);
+
+ if (currentVariableName < lastVariableName) {
+ context.report({
+ node: decl,
+ message: "Variables within the same declaration block should be sorted alphabetically.",
+ fix(fixer) {
+ if (unfixable || fixed) {
+ return null;
+ }
+ return fixer.replaceTextRange(
+ [idDeclarations[0].range[0], idDeclarations[idDeclarations.length - 1].range[1]],
+ idDeclarations
+
+ // Clone the idDeclarations array to avoid mutating it
+ .slice()
+
+ // Sort the array into the desired order
+ .sort((declA, declB) => {
+ const aName = getSortableName(declA);
+ const bName = getSortableName(declB);
+
+ return aName > bName ? 1 : -1;
+ })
+
+ // Build a string out of the sorted list of identifier declarations and the text between the originals
+ .reduce((sourceText, identifier, index) => {
+ const textAfterIdentifier = index === idDeclarations.length - 1
+ ? ""
+ : sourceCode.getText().slice(idDeclarations[index].range[1], idDeclarations[index + 1].range[0]);
+
+ return sourceText + sourceCode.getText(identifier) + textAfterIdentifier;
+ }, "")
+
+ );
+ }
+ });
+ fixed = true;
+ return memo;
+ }
+ return decl;
+
+ }, idDeclarations[0]);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/space-before-blocks.js b/tools/node_modules/eslint/lib/rules/space-before-blocks.js
new file mode 100644
index 0000000000..f50298c9c4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/space-before-blocks.js
@@ -0,0 +1,148 @@
+/**
+ * @fileoverview A rule to ensure whitespace before blocks.
+ * @author Mathias Schreck <https://github.com/lo1tuma>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before blocks",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ keywords: {
+ enum: ["always", "never"]
+ },
+ functions: {
+ enum: ["always", "never"]
+ },
+ classes: {
+ enum: ["always", "never"]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const config = context.options[0],
+ sourceCode = context.getSourceCode();
+ let checkFunctions = true,
+ checkKeywords = true,
+ checkClasses = true;
+
+ if (typeof config === "object") {
+ checkFunctions = config.functions !== "never";
+ checkKeywords = config.keywords !== "never";
+ checkClasses = config.classes !== "never";
+ } else if (config === "never") {
+ checkFunctions = false;
+ checkKeywords = false;
+ checkClasses = false;
+ }
+
+ /**
+ * Checks whether or not a given token is an arrow operator (=>) or a keyword
+ * in order to avoid to conflict with `arrow-spacing` and `keyword-spacing`.
+ *
+ * @param {Token} token - A token to check.
+ * @returns {boolean} `true` if the token is an arrow operator.
+ */
+ function isConflicted(token) {
+ return (token.type === "Punctuator" && token.value === "=>") || token.type === "Keyword";
+ }
+
+ /**
+ * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.
+ * @param {ASTNode|Token} node The AST node of a BlockStatement.
+ * @returns {void} undefined.
+ */
+ function checkPrecedingSpace(node) {
+ const precedingToken = sourceCode.getTokenBefore(node);
+
+ if (precedingToken && !isConflicted(precedingToken) && astUtils.isTokenOnSameLine(precedingToken, node)) {
+ const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);
+ const parent = context.getAncestors().pop();
+ let requireSpace;
+
+ if (parent.type === "FunctionExpression" || parent.type === "FunctionDeclaration") {
+ requireSpace = checkFunctions;
+ } else if (node.type === "ClassBody") {
+ requireSpace = checkClasses;
+ } else {
+ requireSpace = checkKeywords;
+ }
+
+ if (requireSpace) {
+ if (!hasSpace) {
+ context.report({
+ node,
+ message: "Missing space before opening brace.",
+ fix(fixer) {
+ return fixer.insertTextBefore(node, " ");
+ }
+ });
+ }
+ } else {
+ if (hasSpace) {
+ context.report({
+ node,
+ message: "Unexpected space before opening brace.",
+ fix(fixer) {
+ return fixer.removeRange([precedingToken.range[1], node.range[0]]);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if the CaseBlock of an given SwitchStatement node has a preceding space.
+ * @param {ASTNode} node The node of a SwitchStatement.
+ * @returns {void} undefined.
+ */
+ function checkSpaceBeforeCaseBlock(node) {
+ const cases = node.cases;
+ let openingBrace;
+
+ if (cases.length > 0) {
+ openingBrace = sourceCode.getTokenBefore(cases[0]);
+ } else {
+ openingBrace = sourceCode.getLastToken(node, 1);
+ }
+
+ checkPrecedingSpace(openingBrace);
+ }
+
+ return {
+ BlockStatement: checkPrecedingSpace,
+ ClassBody: checkPrecedingSpace,
+ SwitchStatement: checkSpaceBeforeCaseBlock
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/space-before-function-paren.js b/tools/node_modules/eslint/lib/rules/space-before-function-paren.js
new file mode 100644
index 0000000000..8851c4587a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/space-before-function-paren.js
@@ -0,0 +1,142 @@
+/**
+ * @fileoverview Rule to validate spacing before function paren.
+ * @author Mathias Schreck <https://github.com/lo1tuma>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before `function` definition opening parenthesis",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ anonymous: {
+ enum: ["always", "never", "ignore"]
+ },
+ named: {
+ enum: ["always", "never", "ignore"]
+ },
+ asyncArrow: {
+ enum: ["always", "never", "ignore"]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const baseConfig = typeof context.options[0] === "string" ? context.options[0] : "always";
+ const overrideConfig = typeof context.options[0] === "object" ? context.options[0] : {};
+
+ /**
+ * Determines whether a function has a name.
+ * @param {ASTNode} node The function node.
+ * @returns {boolean} Whether the function has a name.
+ */
+ function isNamedFunction(node) {
+ if (node.id) {
+ return true;
+ }
+
+ const parent = node.parent;
+
+ return parent.type === "MethodDefinition" ||
+ (parent.type === "Property" &&
+ (
+ parent.kind === "get" ||
+ parent.kind === "set" ||
+ parent.method
+ )
+ );
+ }
+
+ /**
+ * Gets the config for a given function
+ * @param {ASTNode} node The function node
+ * @returns {string} "always", "never", or "ignore"
+ */
+ function getConfigForFunction(node) {
+ if (node.type === "ArrowFunctionExpression") {
+
+ // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar
+ if (node.async && astUtils.isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 }))) {
+ return overrideConfig.asyncArrow || baseConfig;
+ }
+ } else if (isNamedFunction(node)) {
+ return overrideConfig.named || baseConfig;
+
+ // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
+ } else if (!node.generator) {
+ return overrideConfig.anonymous || baseConfig;
+ }
+
+ return "ignore";
+ }
+
+ /**
+ * Checks the parens of a function node
+ * @param {ASTNode} node A function node
+ * @returns {void}
+ */
+ function checkFunction(node) {
+ const functionConfig = getConfigForFunction(node);
+
+ if (functionConfig === "ignore") {
+ return;
+ }
+
+ const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
+ const leftToken = sourceCode.getTokenBefore(rightToken);
+ const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken);
+
+ if (hasSpacing && functionConfig === "never") {
+ context.report({
+ node,
+ loc: leftToken.loc.end,
+ message: "Unexpected space before function parentheses.",
+ fix: fixer => fixer.removeRange([leftToken.range[1], rightToken.range[0]])
+ });
+ } else if (!hasSpacing && functionConfig === "always") {
+ context.report({
+ node,
+ loc: leftToken.loc.end,
+ message: "Missing space before function parentheses.",
+ fix: fixer => fixer.insertTextAfter(leftToken, " ")
+ });
+ }
+ }
+
+ return {
+ ArrowFunctionExpression: checkFunction,
+ FunctionDeclaration: checkFunction,
+ FunctionExpression: checkFunction
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/space-in-parens.js b/tools/node_modules/eslint/lib/rules/space-in-parens.js
new file mode 100644
index 0000000000..67ec5847cd
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/space-in-parens.js
@@ -0,0 +1,274 @@
+/**
+ * @fileoverview Disallows or enforces spaces inside of parentheses.
+ * @author Jonathan Rajavuori
+ */
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing inside parentheses",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: {
+ enum: ["{}", "[]", "()", "empty"]
+ },
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const MISSING_SPACE_MESSAGE = "There must be a space inside this paren.",
+ REJECTED_SPACE_MESSAGE = "There should be no spaces inside this paren.",
+ ALWAYS = context.options[0] === "always",
+ exceptionsArrayOptions = (context.options[1] && context.options[1].exceptions) || [],
+ options = {};
+ let exceptions;
+
+ if (exceptionsArrayOptions.length) {
+ options.braceException = exceptionsArrayOptions.indexOf("{}") !== -1;
+ options.bracketException = exceptionsArrayOptions.indexOf("[]") !== -1;
+ options.parenException = exceptionsArrayOptions.indexOf("()") !== -1;
+ options.empty = exceptionsArrayOptions.indexOf("empty") !== -1;
+ }
+
+ /**
+ * Produces an object with the opener and closer exception values
+ * @param {Object} opts The exception options
+ * @returns {Object} `openers` and `closers` exception values
+ * @private
+ */
+ function getExceptions() {
+ const openers = [],
+ closers = [];
+
+ if (options.braceException) {
+ openers.push("{");
+ closers.push("}");
+ }
+
+ if (options.bracketException) {
+ openers.push("[");
+ closers.push("]");
+ }
+
+ if (options.parenException) {
+ openers.push("(");
+ closers.push(")");
+ }
+
+ if (options.empty) {
+ openers.push(")");
+ closers.push("(");
+ }
+
+ return {
+ openers,
+ closers
+ };
+ }
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Determines if a token is one of the exceptions for the opener paren
+ * @param {Object} token The token to check
+ * @returns {boolean} True if the token is one of the exceptions for the opener paren
+ */
+ function isOpenerException(token) {
+ return token.type === "Punctuator" && exceptions.openers.indexOf(token.value) >= 0;
+ }
+
+ /**
+ * Determines if a token is one of the exceptions for the closer paren
+ * @param {Object} token The token to check
+ * @returns {boolean} True if the token is one of the exceptions for the closer paren
+ */
+ function isCloserException(token) {
+ return token.type === "Punctuator" && exceptions.closers.indexOf(token.value) >= 0;
+ }
+
+ /**
+ * Determines if an opener paren should have a missing space after it
+ * @param {Object} left The paren token
+ * @param {Object} right The token after it
+ * @returns {boolean} True if the paren should have a space
+ */
+ function shouldOpenerHaveSpace(left, right) {
+ if (sourceCode.isSpaceBetweenTokens(left, right)) {
+ return false;
+ }
+
+ if (ALWAYS) {
+ if (astUtils.isClosingParenToken(right)) {
+ return false;
+ }
+ return !isOpenerException(right);
+ }
+ return isOpenerException(right);
+
+ }
+
+ /**
+ * Determines if an closer paren should have a missing space after it
+ * @param {Object} left The token before the paren
+ * @param {Object} right The paren token
+ * @returns {boolean} True if the paren should have a space
+ */
+ function shouldCloserHaveSpace(left, right) {
+ if (astUtils.isOpeningParenToken(left)) {
+ return false;
+ }
+
+ if (sourceCode.isSpaceBetweenTokens(left, right)) {
+ return false;
+ }
+
+ if (ALWAYS) {
+ return !isCloserException(left);
+ }
+ return isCloserException(left);
+
+ }
+
+ /**
+ * Determines if an opener paren should not have an existing space after it
+ * @param {Object} left The paren token
+ * @param {Object} right The token after it
+ * @returns {boolean} True if the paren should reject the space
+ */
+ function shouldOpenerRejectSpace(left, right) {
+ if (right.type === "Line") {
+ return false;
+ }
+
+ if (!astUtils.isTokenOnSameLine(left, right)) {
+ return false;
+ }
+
+ if (!sourceCode.isSpaceBetweenTokens(left, right)) {
+ return false;
+ }
+
+ if (ALWAYS) {
+ return isOpenerException(right);
+ }
+ return !isOpenerException(right);
+
+ }
+
+ /**
+ * Determines if an closer paren should not have an existing space after it
+ * @param {Object} left The token before the paren
+ * @param {Object} right The paren token
+ * @returns {boolean} True if the paren should reject the space
+ */
+ function shouldCloserRejectSpace(left, right) {
+ if (astUtils.isOpeningParenToken(left)) {
+ return false;
+ }
+
+ if (!astUtils.isTokenOnSameLine(left, right)) {
+ return false;
+ }
+
+ if (!sourceCode.isSpaceBetweenTokens(left, right)) {
+ return false;
+ }
+
+ if (ALWAYS) {
+ return isCloserException(left);
+ }
+ return !isCloserException(left);
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ Program: function checkParenSpaces(node) {
+ exceptions = getExceptions();
+ const tokens = sourceCode.tokensAndComments;
+
+ tokens.forEach((token, i) => {
+ const prevToken = tokens[i - 1];
+ const nextToken = tokens[i + 1];
+
+ if (!astUtils.isOpeningParenToken(token) && !astUtils.isClosingParenToken(token)) {
+ return;
+ }
+
+ if (token.value === "(" && shouldOpenerHaveSpace(token, nextToken)) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: MISSING_SPACE_MESSAGE,
+ fix(fixer) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ });
+ } else if (token.value === "(" && shouldOpenerRejectSpace(token, nextToken)) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: REJECTED_SPACE_MESSAGE,
+ fix(fixer) {
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ }
+ });
+ } else if (token.value === ")" && shouldCloserHaveSpace(prevToken, token)) {
+
+ // context.report(node, token.loc.start, MISSING_SPACE_MESSAGE);
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: MISSING_SPACE_MESSAGE,
+ fix(fixer) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ });
+ } else if (token.value === ")" && shouldCloserRejectSpace(prevToken, token)) {
+ context.report({
+ node,
+ loc: token.loc.start,
+ message: REJECTED_SPACE_MESSAGE,
+ fix(fixer) {
+ return fixer.removeRange([prevToken.range[1], token.range[0]]);
+ }
+ });
+ }
+ });
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/space-infix-ops.js b/tools/node_modules/eslint/lib/rules/space-infix-ops.js
new file mode 100644
index 0000000000..b92c889c7f
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/space-infix-ops.js
@@ -0,0 +1,167 @@
+/**
+ * @fileoverview Require spaces around infix operators
+ * @author Michael Ficarra
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require spacing around infix operators",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ int32Hint: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
+
+ const OPERATORS = [
+ "*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in",
+ "instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=",
+ "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
+ "?", ":", ",", "**"
+ ];
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Returns the first token which violates the rule
+ * @param {ASTNode} left - The left node of the main node
+ * @param {ASTNode} right - The right node of the main node
+ * @returns {Object} The violator token or null
+ * @private
+ */
+ function getFirstNonSpacedToken(left, right) {
+ const tokens = sourceCode.getTokensBetween(left, right, 1);
+
+ for (let i = 1, l = tokens.length - 1; i < l; ++i) {
+ const op = tokens[i];
+
+ if (
+ (op.type === "Punctuator" || op.type === "Keyword") &&
+ OPERATORS.indexOf(op.value) >= 0 &&
+ (tokens[i - 1].range[1] >= op.range[0] || op.range[1] >= tokens[i + 1].range[0])
+ ) {
+ return op;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Reports an AST node as a rule violation
+ * @param {ASTNode} mainNode - The node to report
+ * @param {Object} culpritToken - The token which has a problem
+ * @returns {void}
+ * @private
+ */
+ function report(mainNode, culpritToken) {
+ context.report({
+ node: mainNode,
+ loc: culpritToken.loc.start,
+ message: "Infix operators must be spaced.",
+ fix(fixer) {
+ const previousToken = sourceCode.getTokenBefore(culpritToken);
+ const afterToken = sourceCode.getTokenAfter(culpritToken);
+ let fixString = "";
+
+ if (culpritToken.range[0] - previousToken.range[1] === 0) {
+ fixString = " ";
+ }
+
+ fixString += culpritToken.value;
+
+ if (afterToken.range[0] - culpritToken.range[1] === 0) {
+ fixString += " ";
+ }
+
+ return fixer.replaceText(culpritToken, fixString);
+ }
+ });
+ }
+
+ /**
+ * Check if the node is binary then report
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkBinary(node) {
+ const leftNode = (node.left.typeAnnotation) ? node.left.typeAnnotation : node.left;
+ const rightNode = node.right;
+
+ const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
+
+ if (nonSpacedNode) {
+ if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) {
+ report(node, nonSpacedNode);
+ }
+ }
+ }
+
+ /**
+ * Check if the node is conditional
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkConditional(node) {
+ const nonSpacedConsequesntNode = getFirstNonSpacedToken(node.test, node.consequent);
+ const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate);
+
+ if (nonSpacedConsequesntNode) {
+ report(node, nonSpacedConsequesntNode);
+ } else if (nonSpacedAlternateNode) {
+ report(node, nonSpacedAlternateNode);
+ }
+ }
+
+ /**
+ * Check if the node is a variable
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkVar(node) {
+ const leftNode = (node.id.typeAnnotation) ? node.id.typeAnnotation : node.id;
+ const rightNode = node.init;
+
+ if (rightNode) {
+ const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
+
+ if (nonSpacedNode) {
+ report(node, nonSpacedNode);
+ }
+ }
+ }
+
+ return {
+ AssignmentExpression: checkBinary,
+ AssignmentPattern: checkBinary,
+ BinaryExpression: checkBinary,
+ LogicalExpression: checkBinary,
+ ConditionalExpression: checkConditional,
+ VariableDeclarator: checkVar
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/space-unary-ops.js b/tools/node_modules/eslint/lib/rules/space-unary-ops.js
new file mode 100644
index 0000000000..06a70fb6ad
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/space-unary-ops.js
@@ -0,0 +1,319 @@
+/**
+ * @fileoverview This rule shoud require or disallow spaces before or after unary operations.
+ * @author Marcin Kumorek
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing before or after unary operators",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ words: {
+ type: "boolean"
+ },
+ nonwords: {
+ type: "boolean"
+ },
+ overrides: {
+ type: "object",
+ additionalProperties: {
+ type: "boolean"
+ }
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const options = context.options && Array.isArray(context.options) && context.options[0] || { words: true, nonwords: false };
+
+ const sourceCode = context.getSourceCode();
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Check if the node is the first "!" in a "!!" convert to Boolean expression
+ * @param {ASTnode} node AST node
+ * @returns {boolean} Whether or not the node is first "!" in "!!"
+ */
+ function isFirstBangInBangBangExpression(node) {
+ return node && node.type === "UnaryExpression" && node.argument.operator === "!" &&
+ node.argument && node.argument.type === "UnaryExpression" && node.argument.operator === "!";
+ }
+
+ /**
+ * Check if the node's child argument is an "ObjectExpression"
+ * @param {ASTnode} node AST node
+ * @returns {boolean} Whether or not the argument's type is "ObjectExpression"
+ */
+ function isArgumentObjectExpression(node) {
+ return node.argument && node.argument.type && node.argument.type === "ObjectExpression";
+ }
+
+ /**
+ * Checks if an override exists for a given operator.
+ * @param {string} operator Operator
+ * @returns {boolean} Whether or not an override has been provided for the operator
+ */
+ function overrideExistsForOperator(operator) {
+ return options.overrides && options.overrides.hasOwnProperty(operator);
+ }
+
+ /**
+ * Gets the value that the override was set to for this operator
+ * @param {string} operator Operator
+ * @returns {boolean} Whether or not an override enforces a space with this operator
+ */
+ function overrideEnforcesSpaces(operator) {
+ return options.overrides[operator];
+ }
+
+ /**
+ * Verify Unary Word Operator has spaces after the word operator
+ * @param {ASTnode} node AST node
+ * @param {Object} firstToken first token from the AST node
+ * @param {Object} secondToken second token from the AST node
+ * @param {string} word The word to be used for reporting
+ * @returns {void}
+ */
+ function verifyWordHasSpaces(node, firstToken, secondToken, word) {
+ if (secondToken.range[0] === firstToken.range[1]) {
+ context.report({
+ node,
+ message: "Unary word operator '{{word}}' must be followed by whitespace.",
+ data: {
+ word
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(firstToken, " ");
+ }
+ });
+ }
+ }
+
+ /**
+ * Verify Unary Word Operator doesn't have spaces after the word operator
+ * @param {ASTnode} node AST node
+ * @param {Object} firstToken first token from the AST node
+ * @param {Object} secondToken second token from the AST node
+ * @param {string} word The word to be used for reporting
+ * @returns {void}
+ */
+ function verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word) {
+ if (isArgumentObjectExpression(node)) {
+ if (secondToken.range[0] > firstToken.range[1]) {
+ context.report({
+ node,
+ message: "Unexpected space after unary word operator '{{word}}'.",
+ data: {
+ word
+ },
+ fix(fixer) {
+ return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Check Unary Word Operators for spaces after the word operator
+ * @param {ASTnode} node AST node
+ * @param {Object} firstToken first token from the AST node
+ * @param {Object} secondToken second token from the AST node
+ * @param {string} word The word to be used for reporting
+ * @returns {void}
+ */
+ function checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, word) {
+ word = word || firstToken.value;
+
+ if (overrideExistsForOperator(word)) {
+ if (overrideEnforcesSpaces(word)) {
+ verifyWordHasSpaces(node, firstToken, secondToken, word);
+ } else {
+ verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);
+ }
+ } else if (options.words) {
+ verifyWordHasSpaces(node, firstToken, secondToken, word);
+ } else {
+ verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);
+ }
+ }
+
+ /**
+ * Verifies YieldExpressions satisfy spacing requirements
+ * @param {ASTnode} node AST node
+ * @returns {void}
+ */
+ function checkForSpacesAfterYield(node) {
+ const tokens = sourceCode.getFirstTokens(node, 3),
+ word = "yield";
+
+ if (!node.argument || node.delegate) {
+ return;
+ }
+
+ checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], word);
+ }
+
+ /**
+ * Verifies AwaitExpressions satisfy spacing requirements
+ * @param {ASTNode} node AwaitExpression AST node
+ * @returns {void}
+ */
+ function checkForSpacesAfterAwait(node) {
+ const tokens = sourceCode.getFirstTokens(node, 3);
+
+ checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], "await");
+ }
+
+ /**
+ * Verifies UnaryExpression, UpdateExpression and NewExpression have spaces before or after the operator
+ * @param {ASTnode} node AST node
+ * @param {Object} firstToken First token in the expression
+ * @param {Object} secondToken Second token in the expression
+ * @returns {void}
+ */
+ function verifyNonWordsHaveSpaces(node, firstToken, secondToken) {
+ if (node.prefix) {
+ if (isFirstBangInBangBangExpression(node)) {
+ return;
+ }
+ if (firstToken.range[1] === secondToken.range[0]) {
+ context.report({
+ node,
+ message: "Unary operator '{{operator}}' must be followed by whitespace.",
+ data: {
+ operator: firstToken.value
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(firstToken, " ");
+ }
+ });
+ }
+ } else {
+ if (firstToken.range[1] === secondToken.range[0]) {
+ context.report({
+ node,
+ message: "Space is required before unary expressions '{{token}}'.",
+ data: {
+ token: secondToken.value
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(secondToken, " ");
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Verifies UnaryExpression, UpdateExpression and NewExpression don't have spaces before or after the operator
+ * @param {ASTnode} node AST node
+ * @param {Object} firstToken First token in the expression
+ * @param {Object} secondToken Second token in the expression
+ * @returns {void}
+ */
+ function verifyNonWordsDontHaveSpaces(node, firstToken, secondToken) {
+ if (node.prefix) {
+ if (secondToken.range[0] > firstToken.range[1]) {
+ context.report({
+ node,
+ message: "Unexpected space after unary operator '{{operator}}'.",
+ data: {
+ operator: firstToken.value
+ },
+ fix(fixer) {
+ if (astUtils.canTokensBeAdjacent(firstToken, secondToken)) {
+ return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
+ }
+ return null;
+ }
+ });
+ }
+ } else {
+ if (secondToken.range[0] > firstToken.range[1]) {
+ context.report({
+ node,
+ message: "Unexpected space before unary operator '{{operator}}'.",
+ data: {
+ operator: secondToken.value
+ },
+ fix(fixer) {
+ return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Verifies UnaryExpression, UpdateExpression and NewExpression satisfy spacing requirements
+ * @param {ASTnode} node AST node
+ * @returns {void}
+ */
+ function checkForSpaces(node) {
+ const tokens = node.type === "UpdateExpression" && !node.prefix
+ ? sourceCode.getLastTokens(node, 2)
+ : sourceCode.getFirstTokens(node, 2);
+ const firstToken = tokens[0];
+ const secondToken = tokens[1];
+
+ if ((node.type === "NewExpression" || node.prefix) && firstToken.type === "Keyword") {
+ checkUnaryWordOperatorForSpaces(node, firstToken, secondToken);
+ return;
+ }
+
+ const operator = node.prefix ? tokens[0].value : tokens[1].value;
+
+ if (overrideExistsForOperator(operator)) {
+ if (overrideEnforcesSpaces(operator)) {
+ verifyNonWordsHaveSpaces(node, firstToken, secondToken);
+ } else {
+ verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);
+ }
+ } else if (options.nonwords) {
+ verifyNonWordsHaveSpaces(node, firstToken, secondToken);
+ } else {
+ verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ UnaryExpression: checkForSpaces,
+ UpdateExpression: checkForSpaces,
+ NewExpression: checkForSpaces,
+ YieldExpression: checkForSpacesAfterYield,
+ AwaitExpression: checkForSpacesAfterAwait
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/spaced-comment.js b/tools/node_modules/eslint/lib/rules/spaced-comment.js
new file mode 100644
index 0000000000..8cdd05514c
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/spaced-comment.js
@@ -0,0 +1,375 @@
+/**
+ * @fileoverview Source code for spaced-comments rule
+ * @author Gyandeep Singh
+ */
+"use strict";
+
+const lodash = require("lodash");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Escapes the control characters of a given string.
+ * @param {string} s - A string to escape.
+ * @returns {string} An escaped string.
+ */
+function escape(s) {
+ const isOneChar = s.length === 1;
+
+ s = lodash.escapeRegExp(s);
+ return isOneChar ? s : `(?:${s})`;
+}
+
+/**
+ * Escapes the control characters of a given string.
+ * And adds a repeat flag.
+ * @param {string} s - A string to escape.
+ * @returns {string} An escaped string.
+ */
+function escapeAndRepeat(s) {
+ return `${escape(s)}+`;
+}
+
+/**
+ * Parses `markers` option.
+ * If markers don't include `"*"`, this adds `"*"` to allow JSDoc comments.
+ * @param {string[]} [markers] - A marker list.
+ * @returns {string[]} A marker list.
+ */
+function parseMarkersOption(markers) {
+ markers = markers ? markers.slice(0) : [];
+
+ // `*` is a marker for JSDoc comments.
+ if (markers.indexOf("*") === -1) {
+ markers.push("*");
+ }
+
+ return markers;
+}
+
+/**
+ * Creates string pattern for exceptions.
+ * Generated pattern:
+ *
+ * 1. A space or an exception pattern sequence.
+ *
+ * @param {string[]} exceptions - An exception pattern list.
+ * @returns {string} A regular expression string for exceptions.
+ */
+function createExceptionsPattern(exceptions) {
+ let pattern = "";
+
+ /*
+ * A space or an exception pattern sequence.
+ * [] ==> "\s"
+ * ["-"] ==> "(?:\s|\-+$)"
+ * ["-", "="] ==> "(?:\s|(?:\-+|=+)$)"
+ * ["-", "=", "--=="] ==> "(?:\s|(?:\-+|=+|(?:\-\-==)+)$)" ==> https://jex.im/regulex/#!embed=false&flags=&re=(%3F%3A%5Cs%7C(%3F%3A%5C-%2B%7C%3D%2B%7C(%3F%3A%5C-%5C-%3D%3D)%2B)%24)
+ */
+ if (exceptions.length === 0) {
+
+ // a space.
+ pattern += "\\s";
+ } else {
+
+ // a space or...
+ pattern += "(?:\\s|";
+
+ if (exceptions.length === 1) {
+
+ // a sequence of the exception pattern.
+ pattern += escapeAndRepeat(exceptions[0]);
+ } else {
+
+ // a sequence of one of the exception patterns.
+ pattern += "(?:";
+ pattern += exceptions.map(escapeAndRepeat).join("|");
+ pattern += ")";
+ }
+ pattern += `(?:$|[${Array.from(astUtils.LINEBREAKS).join("")}]))`;
+ }
+
+ return pattern;
+}
+
+/**
+ * Creates RegExp object for `always` mode.
+ * Generated pattern for beginning of comment:
+ *
+ * 1. First, a marker or nothing.
+ * 2. Next, a space or an exception pattern sequence.
+ *
+ * @param {string[]} markers - A marker list.
+ * @param {string[]} exceptions - An exception pattern list.
+ * @returns {RegExp} A RegExp object for the beginning of a comment in `always` mode.
+ */
+function createAlwaysStylePattern(markers, exceptions) {
+ let pattern = "^";
+
+ /*
+ * A marker or nothing.
+ * ["*"] ==> "\*?"
+ * ["*", "!"] ==> "(?:\*|!)?"
+ * ["*", "/", "!<"] ==> "(?:\*|\/|(?:!<))?" ==> https://jex.im/regulex/#!embed=false&flags=&re=(%3F%3A%5C*%7C%5C%2F%7C(%3F%3A!%3C))%3F
+ */
+ if (markers.length === 1) {
+
+ // the marker.
+ pattern += escape(markers[0]);
+ } else {
+
+ // one of markers.
+ pattern += "(?:";
+ pattern += markers.map(escape).join("|");
+ pattern += ")";
+ }
+
+ pattern += "?"; // or nothing.
+ pattern += createExceptionsPattern(exceptions);
+
+ return new RegExp(pattern);
+}
+
+/**
+ * Creates RegExp object for `never` mode.
+ * Generated pattern for beginning of comment:
+ *
+ * 1. First, a marker or nothing (captured).
+ * 2. Next, a space or a tab.
+ *
+ * @param {string[]} markers - A marker list.
+ * @returns {RegExp} A RegExp object for `never` mode.
+ */
+function createNeverStylePattern(markers) {
+ const pattern = `^(${markers.map(escape).join("|")})?[ \t]+`;
+
+ return new RegExp(pattern);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce consistent spacing after the `//` or `/*` in a comment",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ markers: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ line: {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ markers: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ }
+ },
+ additionalProperties: false
+ },
+ block: {
+ type: "object",
+ properties: {
+ exceptions: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ markers: {
+ type: "array",
+ items: {
+ type: "string"
+ }
+ },
+ balanced: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const sourceCode = context.getSourceCode();
+
+ // Unless the first option is never, require a space
+ const requireSpace = context.options[0] !== "never";
+
+ /*
+ * Parse the second options.
+ * If markers don't include `"*"`, it's added automatically for JSDoc
+ * comments.
+ */
+ const config = context.options[1] || {};
+ const balanced = config.block && config.block.balanced;
+
+ const styleRules = ["block", "line"].reduce((rule, type) => {
+ const markers = parseMarkersOption(config[type] && config[type].markers || config.markers);
+ const exceptions = config[type] && config[type].exceptions || config.exceptions || [];
+ const endNeverPattern = "[ \t]+$";
+
+ // Create RegExp object for valid patterns.
+ rule[type] = {
+ beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers),
+ endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`) : new RegExp(endNeverPattern),
+ hasExceptions: exceptions.length > 0,
+ markers: new RegExp(`^(${markers.map(escape).join("|")})`)
+ };
+
+ return rule;
+ }, {});
+
+ /**
+ * Reports a beginning spacing error with an appropriate message.
+ * @param {ASTNode} node - A comment node to check.
+ * @param {string} message - An error message to report.
+ * @param {Array} match - An array of match results for markers.
+ * @param {string} refChar - Character used for reference in the error message.
+ * @returns {void}
+ */
+ function reportBegin(node, message, match, refChar) {
+ const type = node.type.toLowerCase(),
+ commentIdentifier = type === "block" ? "/*" : "//";
+
+ context.report({
+ node,
+ fix(fixer) {
+ const start = node.range[0];
+ let end = start + 2;
+
+ if (requireSpace) {
+ if (match) {
+ end += match[0].length;
+ }
+ return fixer.insertTextAfterRange([start, end], " ");
+ }
+ end += match[0].length;
+ return fixer.replaceTextRange([start, end], commentIdentifier + (match[1] ? match[1] : ""));
+
+ },
+ message,
+ data: { refChar }
+ });
+ }
+
+ /**
+ * Reports an ending spacing error with an appropriate message.
+ * @param {ASTNode} node - A comment node to check.
+ * @param {string} message - An error message to report.
+ * @param {string} match - An array of the matched whitespace characters.
+ * @returns {void}
+ */
+ function reportEnd(node, message, match) {
+ context.report({
+ node,
+ fix(fixer) {
+ if (requireSpace) {
+ return fixer.insertTextAfterRange([node.range[0], node.range[1] - 2], " ");
+ }
+ const end = node.range[1] - 2,
+ start = end - match[0].length;
+
+ return fixer.replaceTextRange([start, end], "");
+
+ },
+ message
+ });
+ }
+
+ /**
+ * Reports a given comment if it's invalid.
+ * @param {ASTNode} node - a comment node to check.
+ * @returns {void}
+ */
+ function checkCommentForSpace(node) {
+ const type = node.type.toLowerCase(),
+ rule = styleRules[type],
+ commentIdentifier = type === "block" ? "/*" : "//";
+
+ // Ignores empty comments.
+ if (node.value.length === 0) {
+ return;
+ }
+
+ const beginMatch = rule.beginRegex.exec(node.value);
+ const endMatch = rule.endRegex.exec(node.value);
+
+ // Checks.
+ if (requireSpace) {
+ if (!beginMatch) {
+ const hasMarker = rule.markers.exec(node.value);
+ const marker = hasMarker ? commentIdentifier + hasMarker[0] : commentIdentifier;
+
+ if (rule.hasExceptions) {
+ reportBegin(node, "Expected exception block, space or tab after '{{refChar}}' in comment.", hasMarker, marker);
+ } else {
+ reportBegin(node, "Expected space or tab after '{{refChar}}' in comment.", hasMarker, marker);
+ }
+ }
+
+ if (balanced && type === "block" && !endMatch) {
+ reportEnd(node, "Expected space or tab before '*/' in comment.");
+ }
+ } else {
+ if (beginMatch) {
+ if (!beginMatch[1]) {
+ reportBegin(node, "Unexpected space or tab after '{{refChar}}' in comment.", beginMatch, commentIdentifier);
+ } else {
+ reportBegin(node, "Unexpected space or tab after marker ({{refChar}}) in comment.", beginMatch, beginMatch[1]);
+ }
+ }
+
+ if (balanced && type === "block" && endMatch) {
+ reportEnd(node, "Unexpected space or tab before '*/' in comment.", endMatch);
+ }
+ }
+ }
+
+ return {
+ Program() {
+ const comments = sourceCode.getAllComments();
+
+ comments.filter(token => token.type !== "Shebang").forEach(checkCommentForSpace);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/strict.js b/tools/node_modules/eslint/lib/rules/strict.js
new file mode 100644
index 0000000000..433b471367
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/strict.js
@@ -0,0 +1,277 @@
+/**
+ * @fileoverview Rule to control usage of strict mode directives.
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const messages = {
+ function: "Use the function form of 'use strict'.",
+ global: "Use the global form of 'use strict'.",
+ multiple: "Multiple 'use strict' directives.",
+ never: "Strict mode is not permitted.",
+ unnecessary: "Unnecessary 'use strict' directive.",
+ module: "'use strict' is unnecessary inside of modules.",
+ implied: "'use strict' is unnecessary when implied strict mode is enabled.",
+ unnecessaryInClasses: "'use strict' is unnecessary inside of classes.",
+ nonSimpleParameterList: "'use strict' directive inside a function with non-simple parameter list throws a syntax error since ES2016.",
+ wrap: "Wrap {{name}} in a function with 'use strict' directive."
+};
+
+/**
+ * Gets all of the Use Strict Directives in the Directive Prologue of a group of
+ * statements.
+ * @param {ASTNode[]} statements Statements in the program or function body.
+ * @returns {ASTNode[]} All of the Use Strict Directives.
+ */
+function getUseStrictDirectives(statements) {
+ const directives = [];
+
+ for (let i = 0; i < statements.length; i++) {
+ const statement = statements[i];
+
+ if (
+ statement.type === "ExpressionStatement" &&
+ statement.expression.type === "Literal" &&
+ statement.expression.value === "use strict"
+ ) {
+ directives[i] = statement;
+ } else {
+ break;
+ }
+ }
+
+ return directives;
+}
+
+/**
+ * Checks whether a given parameter is a simple parameter.
+ *
+ * @param {ASTNode} node - A pattern node to check.
+ * @returns {boolean} `true` if the node is an Identifier node.
+ */
+function isSimpleParameter(node) {
+ return node.type === "Identifier";
+}
+
+/**
+ * Checks whether a given parameter list is a simple parameter list.
+ *
+ * @param {ASTNode[]} params - A parameter list to check.
+ * @returns {boolean} `true` if the every parameter is an Identifier node.
+ */
+function isSimpleParameterList(params) {
+ return params.every(isSimpleParameter);
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow strict mode directives",
+ category: "Strict Mode",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["never", "global", "function", "safe"]
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const ecmaFeatures = context.parserOptions.ecmaFeatures || {},
+ scopes = [],
+ classScopes = [];
+ let mode = context.options[0] || "safe";
+
+ if (ecmaFeatures.impliedStrict) {
+ mode = "implied";
+ } else if (mode === "safe") {
+ mode = ecmaFeatures.globalReturn ? "global" : "function";
+ }
+
+ /**
+ * Determines whether a reported error should be fixed, depending on the error type.
+ * @param {string} errorType The type of error
+ * @returns {boolean} `true` if the reported error should be fixed
+ */
+ function shouldFix(errorType) {
+ return errorType === "multiple" || errorType === "unnecessary" || errorType === "module" || errorType === "implied" || errorType === "unnecessaryInClasses";
+ }
+
+ /**
+ * Gets a fixer function to remove a given 'use strict' directive.
+ * @param {ASTNode} node The directive that should be removed
+ * @returns {Function} A fixer function
+ */
+ function getFixFunction(node) {
+ return fixer => fixer.remove(node);
+ }
+
+ /**
+ * Report a slice of an array of nodes with a given message.
+ * @param {ASTNode[]} nodes Nodes.
+ * @param {string} start Index to start from.
+ * @param {string} end Index to end before.
+ * @param {string} message Message to display.
+ * @param {boolean} fix `true` if the directive should be fixed (i.e. removed)
+ * @returns {void}
+ */
+ function reportSlice(nodes, start, end, message, fix) {
+ nodes.slice(start, end).forEach(node => {
+ context.report({ node, message, fix: fix ? getFixFunction(node) : null });
+ });
+ }
+
+ /**
+ * Report all nodes in an array with a given message.
+ * @param {ASTNode[]} nodes Nodes.
+ * @param {string} message Message to display.
+ * @param {boolean} fix `true` if the directive should be fixed (i.e. removed)
+ * @returns {void}
+ */
+ function reportAll(nodes, message, fix) {
+ reportSlice(nodes, 0, nodes.length, message, fix);
+ }
+
+ /**
+ * Report all nodes in an array, except the first, with a given message.
+ * @param {ASTNode[]} nodes Nodes.
+ * @param {string} message Message to display.
+ * @param {boolean} fix `true` if the directive should be fixed (i.e. removed)
+ * @returns {void}
+ */
+ function reportAllExceptFirst(nodes, message, fix) {
+ reportSlice(nodes, 1, nodes.length, message, fix);
+ }
+
+ /**
+ * Entering a function in 'function' mode pushes a new nested scope onto the
+ * stack. The new scope is true if the nested function is strict mode code.
+ * @param {ASTNode} node The function declaration or expression.
+ * @param {ASTNode[]} useStrictDirectives The Use Strict Directives of the node.
+ * @returns {void}
+ */
+ function enterFunctionInFunctionMode(node, useStrictDirectives) {
+ const isInClass = classScopes.length > 0,
+ isParentGlobal = scopes.length === 0 && classScopes.length === 0,
+ isParentStrict = scopes.length > 0 && scopes[scopes.length - 1],
+ isStrict = useStrictDirectives.length > 0;
+
+ if (isStrict) {
+ if (!isSimpleParameterList(node.params)) {
+ context.report({ node: useStrictDirectives[0], message: messages.nonSimpleParameterList });
+ } else if (isParentStrict) {
+ context.report({ node: useStrictDirectives[0], message: messages.unnecessary, fix: getFixFunction(useStrictDirectives[0]) });
+ } else if (isInClass) {
+ context.report({ node: useStrictDirectives[0], message: messages.unnecessaryInClasses, fix: getFixFunction(useStrictDirectives[0]) });
+ }
+
+ reportAllExceptFirst(useStrictDirectives, messages.multiple, true);
+ } else if (isParentGlobal) {
+ if (isSimpleParameterList(node.params)) {
+ context.report({ node, message: messages.function });
+ } else {
+ context.report({
+ node,
+ message: messages.wrap,
+ data: { name: astUtils.getFunctionNameWithKind(node) }
+ });
+ }
+ }
+
+ scopes.push(isParentStrict || isStrict);
+ }
+
+ /**
+ * Exiting a function in 'function' mode pops its scope off the stack.
+ * @returns {void}
+ */
+ function exitFunctionInFunctionMode() {
+ scopes.pop();
+ }
+
+ /**
+ * Enter a function and either:
+ * - Push a new nested scope onto the stack (in 'function' mode).
+ * - Report all the Use Strict Directives (in the other modes).
+ * @param {ASTNode} node The function declaration or expression.
+ * @returns {void}
+ */
+ function enterFunction(node) {
+ const isBlock = node.body.type === "BlockStatement",
+ useStrictDirectives = isBlock
+ ? getUseStrictDirectives(node.body.body) : [];
+
+ if (mode === "function") {
+ enterFunctionInFunctionMode(node, useStrictDirectives);
+ } else if (useStrictDirectives.length > 0) {
+ if (isSimpleParameterList(node.params)) {
+ reportAll(useStrictDirectives, messages[mode], shouldFix(mode));
+ } else {
+ context.report({ node: useStrictDirectives[0], message: messages.nonSimpleParameterList });
+ reportAllExceptFirst(useStrictDirectives, messages.multiple, true);
+ }
+ }
+ }
+
+ const rule = {
+ Program(node) {
+ const useStrictDirectives = getUseStrictDirectives(node.body);
+
+ if (node.sourceType === "module") {
+ mode = "module";
+ }
+
+ if (mode === "global") {
+ if (node.body.length > 0 && useStrictDirectives.length === 0) {
+ context.report({ node, message: messages.global });
+ }
+ reportAllExceptFirst(useStrictDirectives, messages.multiple, true);
+ } else {
+ reportAll(useStrictDirectives, messages[mode], shouldFix(mode));
+ }
+ },
+ FunctionDeclaration: enterFunction,
+ FunctionExpression: enterFunction,
+ ArrowFunctionExpression: enterFunction
+ };
+
+ if (mode === "function") {
+ Object.assign(rule, {
+
+ // Inside of class bodies are always strict mode.
+ ClassBody() {
+ classScopes.push(true);
+ },
+ "ClassBody:exit"() {
+ classScopes.pop();
+ },
+
+ "FunctionDeclaration:exit": exitFunctionInFunctionMode,
+ "FunctionExpression:exit": exitFunctionInFunctionMode,
+ "ArrowFunctionExpression:exit": exitFunctionInFunctionMode
+ });
+ }
+
+ return rule;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/switch-colon-spacing.js b/tools/node_modules/eslint/lib/rules/switch-colon-spacing.js
new file mode 100644
index 0000000000..400e850997
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/switch-colon-spacing.js
@@ -0,0 +1,133 @@
+/**
+ * @fileoverview Rule to enforce spacing around colons of switch statements.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce spacing around colons of switch statements",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+ schema: [
+ {
+ type: "object",
+ properties: {
+ before: { type: "boolean" },
+ after: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ],
+ fixable: "whitespace"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const options = context.options[0] || {};
+ const beforeSpacing = options.before === true; // false by default
+ const afterSpacing = options.after !== false; // true by default
+
+ /**
+ * Get the colon token of the given SwitchCase node.
+ * @param {ASTNode} node The SwitchCase node to get.
+ * @returns {Token} The colon token of the node.
+ */
+ function getColonToken(node) {
+ if (node.test) {
+ return sourceCode.getTokenAfter(node.test, astUtils.isColonToken);
+ }
+ return sourceCode.getFirstToken(node, 1);
+ }
+
+ /**
+ * Check whether the spacing between the given 2 tokens is valid or not.
+ * @param {Token} left The left token to check.
+ * @param {Token} right The right token to check.
+ * @param {boolean} expected The expected spacing to check. `true` if there should be a space.
+ * @returns {boolean} `true` if the spacing between the tokens is valid.
+ */
+ function isValidSpacing(left, right, expected) {
+ return (
+ astUtils.isClosingBraceToken(right) ||
+ !astUtils.isTokenOnSameLine(left, right) ||
+ sourceCode.isSpaceBetweenTokens(left, right) === expected
+ );
+ }
+
+ /**
+ * Check whether comments exist between the given 2 tokens.
+ * @param {Token} left The left token to check.
+ * @param {Token} right The right token to check.
+ * @returns {boolean} `true` if comments exist between the given 2 tokens.
+ */
+ function commentsExistBetween(left, right) {
+ return sourceCode.getFirstTokenBetween(
+ left,
+ right,
+ {
+ includeComments: true,
+ filter: astUtils.isCommentToken
+ }
+ ) !== null;
+ }
+
+ /**
+ * Fix the spacing between the given 2 tokens.
+ * @param {RuleFixer} fixer The fixer to fix.
+ * @param {Token} left The left token of fix range.
+ * @param {Token} right The right token of fix range.
+ * @param {boolean} spacing The spacing style. `true` if there should be a space.
+ * @returns {Fix|null} The fix object.
+ */
+ function fix(fixer, left, right, spacing) {
+ if (commentsExistBetween(left, right)) {
+ return null;
+ }
+ if (spacing) {
+ return fixer.insertTextAfter(left, " ");
+ }
+ return fixer.removeRange([left.range[1], right.range[0]]);
+ }
+
+ return {
+ SwitchCase(node) {
+ const colonToken = getColonToken(node);
+ const beforeToken = sourceCode.getTokenBefore(colonToken);
+ const afterToken = sourceCode.getTokenAfter(colonToken);
+
+ if (!isValidSpacing(beforeToken, colonToken, beforeSpacing)) {
+ context.report({
+ node,
+ loc: colonToken.loc,
+ message: "{{verb}} space(s) before this colon.",
+ data: { verb: beforeSpacing ? "Expected" : "Unexpected" },
+ fix: fixer => fix(fixer, beforeToken, colonToken, beforeSpacing)
+ });
+ }
+ if (!isValidSpacing(colonToken, afterToken, afterSpacing)) {
+ context.report({
+ node,
+ loc: colonToken.loc,
+ message: "{{verb}} space(s) after this colon.",
+ data: { verb: afterSpacing ? "Expected" : "Unexpected" },
+ fix: fixer => fix(fixer, colonToken, afterToken, afterSpacing)
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/symbol-description.js b/tools/node_modules/eslint/lib/rules/symbol-description.js
new file mode 100644
index 0000000000..3f5ffd7463
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/symbol-description.js
@@ -0,0 +1,66 @@
+/**
+ * @fileoverview Rule to enforce description with the `Symbol` object
+ * @author Jarek Rencz
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require symbol descriptions",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ /**
+ * Reports if node does not conform the rule in case rule is set to
+ * report missing description
+ *
+ * @param {ASTNode} node - A CallExpression node to check.
+ * @returns {void}
+ */
+ function checkArgument(node) {
+ if (node.arguments.length === 0) {
+ context.report({
+ node,
+ message: "Expected Symbol to have a description."
+ });
+ }
+ }
+
+ return {
+ "Program:exit"() {
+ const scope = context.getScope();
+ const variable = astUtils.getVariableByName(scope, "Symbol");
+
+ if (variable && variable.defs.length === 0) {
+ variable.references.forEach(reference => {
+ const node = reference.identifier;
+
+ if (astUtils.isCallee(node)) {
+ checkArgument(node.parent);
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/template-curly-spacing.js b/tools/node_modules/eslint/lib/rules/template-curly-spacing.js
new file mode 100644
index 0000000000..1d491a24c9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/template-curly-spacing.js
@@ -0,0 +1,121 @@
+/**
+ * @fileoverview Rule to enforce spacing around embedded expressions of template strings
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const OPEN_PAREN = /\$\{$/;
+const CLOSE_PAREN = /^\}/;
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow spacing around embedded expressions of template strings",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ { enum: ["always", "never"] }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const always = context.options[0] === "always";
+ const prefix = always ? "Expected" : "Unexpected";
+
+ /**
+ * Checks spacing before `}` of a given token.
+ * @param {Token} token - A token to check. This is a Template token.
+ * @returns {void}
+ */
+ function checkSpacingBefore(token) {
+ const prevToken = sourceCode.getTokenBefore(token);
+
+ if (prevToken &&
+ CLOSE_PAREN.test(token.value) &&
+ astUtils.isTokenOnSameLine(prevToken, token) &&
+ sourceCode.isSpaceBetweenTokens(prevToken, token) !== always
+ ) {
+ context.report({
+ loc: token.loc.start,
+ message: "{{prefix}} space(s) before '}'.",
+ data: {
+ prefix
+ },
+ fix(fixer) {
+ if (always) {
+ return fixer.insertTextBefore(token, " ");
+ }
+ return fixer.removeRange([
+ prevToken.range[1],
+ token.range[0]
+ ]);
+ }
+ });
+ }
+ }
+
+ /**
+ * Checks spacing after `${` of a given token.
+ * @param {Token} token - A token to check. This is a Template token.
+ * @returns {void}
+ */
+ function checkSpacingAfter(token) {
+ const nextToken = sourceCode.getTokenAfter(token);
+
+ if (nextToken &&
+ OPEN_PAREN.test(token.value) &&
+ astUtils.isTokenOnSameLine(token, nextToken) &&
+ sourceCode.isSpaceBetweenTokens(token, nextToken) !== always
+ ) {
+ context.report({
+ loc: {
+ line: token.loc.end.line,
+ column: token.loc.end.column - 2
+ },
+ message: "{{prefix}} space(s) after '${'.",
+ data: {
+ prefix
+ },
+ fix(fixer) {
+ if (always) {
+ return fixer.insertTextAfter(token, " ");
+ }
+ return fixer.removeRange([
+ token.range[1],
+ nextToken.range[0]
+ ]);
+ }
+ });
+ }
+ }
+
+ return {
+ TemplateElement(node) {
+ const token = sourceCode.getFirstToken(node);
+
+ checkSpacingBefore(token);
+ checkSpacingAfter(token);
+ }
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/template-tag-spacing.js b/tools/node_modules/eslint/lib/rules/template-tag-spacing.js
new file mode 100755
index 0000000000..907c537ff3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/template-tag-spacing.js
@@ -0,0 +1,77 @@
+/**
+ * @fileoverview Rule to check spacing between template tags and their literals
+ * @author Jonathan Wilsson
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow spacing between template tags and their literals",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ { enum: ["always", "never"] }
+ ]
+ },
+
+ create(context) {
+ const never = context.options[0] !== "always";
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Check if a space is present between a template tag and its literal
+ * @param {ASTNode} node node to evaluate
+ * @returns {void}
+ * @private
+ */
+ function checkSpacing(node) {
+ const tagToken = sourceCode.getTokenBefore(node.quasi);
+ const literalToken = sourceCode.getFirstToken(node.quasi);
+ const hasWhitespace = sourceCode.isSpaceBetweenTokens(tagToken, literalToken);
+
+ if (never && hasWhitespace) {
+ context.report({
+ node,
+ loc: tagToken.loc.start,
+ message: "Unexpected space between template tag and template literal.",
+ fix(fixer) {
+ const comments = sourceCode.getCommentsBefore(node.quasi);
+
+ // Don't fix anything if there's a single line comment after the template tag
+ if (comments.some(comment => comment.type === "Line")) {
+ return null;
+ }
+
+ return fixer.replaceTextRange(
+ [tagToken.range[1], literalToken.range[0]],
+ comments.reduce((text, comment) => text + sourceCode.getText(comment), "")
+ );
+ }
+ });
+ } else if (!never && !hasWhitespace) {
+ context.report({
+ node,
+ loc: tagToken.loc.start,
+ message: "Missing space between template tag and template literal.",
+ fix(fixer) {
+ return fixer.insertTextAfter(tagToken, " ");
+ }
+ });
+ }
+ }
+
+ return {
+ TaggedTemplateExpression: checkSpacing
+ };
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/unicode-bom.js b/tools/node_modules/eslint/lib/rules/unicode-bom.js
new file mode 100644
index 0000000000..7109a49edb
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/unicode-bom.js
@@ -0,0 +1,66 @@
+/**
+ * @fileoverview Require or disallow Unicode BOM
+ * @author Andrew Johnston <https://github.com/ehjay>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow Unicode byte order mark (BOM)",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ }
+ ]
+ },
+
+ create(context) {
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ Program: function checkUnicodeBOM(node) {
+
+ const sourceCode = context.getSourceCode(),
+ location = { column: 0, line: 1 },
+ requireBOM = context.options[0] || "never";
+
+ if (!sourceCode.hasBOM && (requireBOM === "always")) {
+ context.report({
+ node,
+ loc: location,
+ message: "Expected Unicode BOM (Byte Order Mark).",
+ fix(fixer) {
+ return fixer.insertTextBeforeRange([0, 1], "\uFEFF");
+ }
+ });
+ } else if (sourceCode.hasBOM && (requireBOM === "never")) {
+ context.report({
+ node,
+ loc: location,
+ message: "Unexpected Unicode BOM (Byte Order Mark).",
+ fix(fixer) {
+ return fixer.removeRange([-1, 0]);
+ }
+ });
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/use-isnan.js b/tools/node_modules/eslint/lib/rules/use-isnan.js
new file mode 100644
index 0000000000..5ec48a0386
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/use-isnan.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview Rule to flag comparisons to the value NaN
+ * @author James Allardice
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require calls to `isNaN()` when checking for `NaN`",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: []
+ },
+
+ create(context) {
+
+ return {
+ BinaryExpression(node) {
+ if (/^(?:[<>]|[!=]=)=?$/.test(node.operator) && (node.left.name === "NaN" || node.right.name === "NaN")) {
+ context.report({ node, message: "Use the isNaN function to compare with NaN." });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/valid-jsdoc.js b/tools/node_modules/eslint/lib/rules/valid-jsdoc.js
new file mode 100644
index 0000000000..ea60b115ce
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/valid-jsdoc.js
@@ -0,0 +1,423 @@
+/**
+ * @fileoverview Validates JSDoc comments are syntactically correct
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const doctrine = require("doctrine");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce valid JSDoc comments",
+ category: "Possible Errors",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ prefer: {
+ type: "object",
+ additionalProperties: {
+ type: "string"
+ }
+ },
+ preferType: {
+ type: "object",
+ additionalProperties: {
+ type: "string"
+ }
+ },
+ requireReturn: {
+ type: "boolean"
+ },
+ requireParamDescription: {
+ type: "boolean"
+ },
+ requireReturnDescription: {
+ type: "boolean"
+ },
+ matchDescription: {
+ type: "string"
+ },
+ requireReturnType: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const options = context.options[0] || {},
+ prefer = options.prefer || {},
+ sourceCode = context.getSourceCode(),
+
+ // these both default to true, so you have to explicitly make them false
+ requireReturn = options.requireReturn !== false,
+ requireParamDescription = options.requireParamDescription !== false,
+ requireReturnDescription = options.requireReturnDescription !== false,
+ requireReturnType = options.requireReturnType !== false,
+ preferType = options.preferType || {},
+ checkPreferType = Object.keys(preferType).length !== 0;
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ // Using a stack to store if a function returns or not (handling nested functions)
+ const fns = [];
+
+ /**
+ * Check if node type is a Class
+ * @param {ASTNode} node node to check.
+ * @returns {boolean} True is its a class
+ * @private
+ */
+ function isTypeClass(node) {
+ return node.type === "ClassExpression" || node.type === "ClassDeclaration";
+ }
+
+ /**
+ * When parsing a new function, store it in our function stack.
+ * @param {ASTNode} node A function node to check.
+ * @returns {void}
+ * @private
+ */
+ function startFunction(node) {
+ fns.push({
+ returnPresent: (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") ||
+ isTypeClass(node)
+ });
+ }
+
+ /**
+ * Indicate that return has been found in the current function.
+ * @param {ASTNode} node The return node.
+ * @returns {void}
+ * @private
+ */
+ function addReturn(node) {
+ const functionState = fns[fns.length - 1];
+
+ if (functionState && node.argument !== null) {
+ functionState.returnPresent = true;
+ }
+ }
+
+ /**
+ * Check if return tag type is void or undefined
+ * @param {Object} tag JSDoc tag
+ * @returns {boolean} True if its of type void or undefined
+ * @private
+ */
+ function isValidReturnType(tag) {
+ return tag.type === null || tag.type.name === "void" || tag.type.type === "UndefinedLiteral";
+ }
+
+ /**
+ * Check if type should be validated based on some exceptions
+ * @param {Object} type JSDoc tag
+ * @returns {boolean} True if it can be validated
+ * @private
+ */
+ function canTypeBeValidated(type) {
+ return type !== "UndefinedLiteral" && // {undefined} as there is no name property available.
+ type !== "NullLiteral" && // {null}
+ type !== "NullableLiteral" && // {?}
+ type !== "FunctionType" && // {function(a)}
+ type !== "AllLiteral"; // {*}
+ }
+
+ /**
+ * Extract the current and expected type based on the input type object
+ * @param {Object} type JSDoc tag
+ * @returns {Object} current and expected type object
+ * @private
+ */
+ function getCurrentExpectedTypes(type) {
+ let currentType;
+
+ if (type.name) {
+ currentType = type.name;
+ } else if (type.expression) {
+ currentType = type.expression.name;
+ }
+
+ const expectedType = currentType && preferType[currentType];
+
+ return {
+ currentType,
+ expectedType
+ };
+ }
+
+ /**
+ * Validate type for a given JSDoc node
+ * @param {Object} jsdocNode JSDoc node
+ * @param {Object} type JSDoc tag
+ * @returns {void}
+ * @private
+ */
+ function validateType(jsdocNode, type) {
+ if (!type || !canTypeBeValidated(type.type)) {
+ return;
+ }
+
+ const typesToCheck = [];
+ let elements = [];
+
+ switch (type.type) {
+ case "TypeApplication": // {Array.<String>}
+ elements = type.applications[0].type === "UnionType" ? type.applications[0].elements : type.applications;
+ typesToCheck.push(getCurrentExpectedTypes(type));
+ break;
+ case "RecordType": // {{20:String}}
+ elements = type.fields;
+ break;
+ case "UnionType": // {String|number|Test}
+ case "ArrayType": // {[String, number, Test]}
+ elements = type.elements;
+ break;
+ case "FieldType": // Array.<{count: number, votes: number}>
+ if (type.value) {
+ typesToCheck.push(getCurrentExpectedTypes(type.value));
+ }
+ break;
+ default:
+ typesToCheck.push(getCurrentExpectedTypes(type));
+ }
+
+ elements.forEach(validateType.bind(null, jsdocNode));
+
+ typesToCheck.forEach(typeToCheck => {
+ if (typeToCheck.expectedType &&
+ typeToCheck.expectedType !== typeToCheck.currentType) {
+ context.report({
+ node: jsdocNode,
+ message: "Use '{{expectedType}}' instead of '{{currentType}}'.",
+ data: {
+ currentType: typeToCheck.currentType,
+ expectedType: typeToCheck.expectedType
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Validate the JSDoc node and output warnings if anything is wrong.
+ * @param {ASTNode} node The AST node to check.
+ * @returns {void}
+ * @private
+ */
+ function checkJSDoc(node) {
+ const jsdocNode = sourceCode.getJSDocComment(node),
+ functionData = fns.pop(),
+ params = Object.create(null),
+ paramsTags = [];
+ let hasReturns = false,
+ returnsTag,
+ hasConstructor = false,
+ isInterface = false,
+ isOverride = false,
+ isAbstract = false;
+
+ // make sure only to validate JSDoc comments
+ if (jsdocNode) {
+ let jsdoc;
+
+ try {
+ jsdoc = doctrine.parse(jsdocNode.value, {
+ strict: true,
+ unwrap: true,
+ sloppy: true
+ });
+ } catch (ex) {
+
+ if (/braces/i.test(ex.message)) {
+ context.report({ node: jsdocNode, message: "JSDoc type missing brace." });
+ } else {
+ context.report({ node: jsdocNode, message: "JSDoc syntax error." });
+ }
+
+ return;
+ }
+
+ jsdoc.tags.forEach(tag => {
+
+ switch (tag.title.toLowerCase()) {
+
+ case "param":
+ case "arg":
+ case "argument":
+ paramsTags.push(tag);
+ break;
+
+ case "return":
+ case "returns":
+ hasReturns = true;
+ returnsTag = tag;
+ break;
+
+ case "constructor":
+ case "class":
+ hasConstructor = true;
+ break;
+
+ case "override":
+ case "inheritdoc":
+ isOverride = true;
+ break;
+
+ case "abstract":
+ case "virtual":
+ isAbstract = true;
+ break;
+
+ case "interface":
+ isInterface = true;
+ break;
+
+ // no default
+ }
+
+ // check tag preferences
+ if (prefer.hasOwnProperty(tag.title) && tag.title !== prefer[tag.title]) {
+ context.report({ node: jsdocNode, message: "Use @{{name}} instead.", data: { name: prefer[tag.title] } });
+ }
+
+ // validate the types
+ if (checkPreferType && tag.type) {
+ validateType(jsdocNode, tag.type);
+ }
+ });
+
+ paramsTags.forEach(param => {
+ if (!param.type) {
+ context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: param.name } });
+ }
+ if (!param.description && requireParamDescription) {
+ context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: param.name } });
+ }
+ if (params[param.name]) {
+ context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: param.name } });
+ } else if (param.name.indexOf(".") === -1) {
+ params[param.name] = 1;
+ }
+ });
+
+ if (hasReturns) {
+ if (!requireReturn && !functionData.returnPresent && (returnsTag.type === null || !isValidReturnType(returnsTag)) && !isAbstract) {
+ context.report({
+ node: jsdocNode,
+ message: "Unexpected @{{title}} tag; function has no return statement.",
+ data: {
+ title: returnsTag.title
+ }
+ });
+ } else {
+ if (requireReturnType && !returnsTag.type) {
+ context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
+ }
+
+ if (!isValidReturnType(returnsTag) && !returnsTag.description && requireReturnDescription) {
+ context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
+ }
+ }
+ }
+
+ // check for functions missing @returns
+ if (!isOverride && !hasReturns && !hasConstructor && !isInterface &&
+ node.parent.kind !== "get" && node.parent.kind !== "constructor" &&
+ node.parent.kind !== "set" && !isTypeClass(node)) {
+ if (requireReturn || functionData.returnPresent) {
+ context.report({
+ node: jsdocNode,
+ message: "Missing JSDoc @{{returns}} for function.",
+ data: {
+ returns: prefer.returns || "returns"
+ }
+ });
+ }
+ }
+
+ // check the parameters
+ const jsdocParams = Object.keys(params);
+
+ if (node.params) {
+ node.params.forEach((param, i) => {
+ if (param.type === "AssignmentPattern") {
+ param = param.left;
+ }
+
+ const name = param.name;
+
+ // TODO(nzakas): Figure out logical things to do with destructured, default, rest params
+ if (param.type === "Identifier") {
+ if (jsdocParams[i] && (name !== jsdocParams[i])) {
+ context.report({
+ node: jsdocNode,
+ message: "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.",
+ data: {
+ name,
+ jsdocName: jsdocParams[i]
+ }
+ });
+ } else if (!params[name] && !isOverride) {
+ context.report({
+ node: jsdocNode,
+ message: "Missing JSDoc for parameter '{{name}}'.",
+ data: {
+ name
+ }
+ });
+ }
+ }
+ });
+ }
+
+ if (options.matchDescription) {
+ const regex = new RegExp(options.matchDescription);
+
+ if (!regex.test(jsdoc.description)) {
+ context.report({ node: jsdocNode, message: "JSDoc description does not satisfy the regex pattern." });
+ }
+ }
+
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ ArrowFunctionExpression: startFunction,
+ FunctionExpression: startFunction,
+ FunctionDeclaration: startFunction,
+ ClassExpression: startFunction,
+ ClassDeclaration: startFunction,
+ "ArrowFunctionExpression:exit": checkJSDoc,
+ "FunctionExpression:exit": checkJSDoc,
+ "FunctionDeclaration:exit": checkJSDoc,
+ "ClassExpression:exit": checkJSDoc,
+ "ClassDeclaration:exit": checkJSDoc,
+ ReturnStatement: addReturn
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/valid-typeof.js b/tools/node_modules/eslint/lib/rules/valid-typeof.js
new file mode 100644
index 0000000000..fba4cc5712
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/valid-typeof.js
@@ -0,0 +1,77 @@
+/**
+ * @fileoverview Ensures that the results of typeof are compared against a valid string
+ * @author Ian Christian Myers
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce comparing `typeof` expressions against valid strings",
+ category: "Possible Errors",
+ recommended: true
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ requireStringLiterals: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+
+ const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function"],
+ OPERATORS = ["==", "===", "!=", "!=="];
+
+ const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
+
+ /**
+ * Determines whether a node is a typeof expression.
+ * @param {ASTNode} node The node
+ * @returns {boolean} `true` if the node is a typeof expression
+ */
+ function isTypeofExpression(node) {
+ return node.type === "UnaryExpression" && node.operator === "typeof";
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+
+ UnaryExpression(node) {
+ if (isTypeofExpression(node)) {
+ const parent = context.getAncestors().pop();
+
+ if (parent.type === "BinaryExpression" && OPERATORS.indexOf(parent.operator) !== -1) {
+ const sibling = parent.left === node ? parent.right : parent.left;
+
+ if (sibling.type === "Literal" || sibling.type === "TemplateLiteral" && !sibling.expressions.length) {
+ const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked;
+
+ if (VALID_TYPES.indexOf(value) === -1) {
+ context.report({ node: sibling, message: "Invalid typeof comparison value." });
+ }
+ } else if (requireStringLiterals && !isTypeofExpression(sibling)) {
+ context.report({ node: sibling, message: "Typeof comparisons should be to string literals." });
+ }
+ }
+ }
+ }
+
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/vars-on-top.js b/tools/node_modules/eslint/lib/rules/vars-on-top.js
new file mode 100644
index 0000000000..f74db905b1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/vars-on-top.js
@@ -0,0 +1,149 @@
+/**
+ * @fileoverview Rule to enforce var declarations are only at the top of a function.
+ * @author Danny Fritz
+ * @author Gyandeep Singh
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require `var` declarations be placed at the top of their containing scope",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: []
+ },
+
+ create(context) {
+ const errorMessage = "All 'var' declarations must be at the top of the function scope.";
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node structurally represents a directive
+ */
+ function looksLikeDirective(node) {
+ return node.type === "ExpressionStatement" &&
+ node.expression.type === "Literal" && typeof node.expression.value === "string";
+ }
+
+ /**
+ * Check to see if its a ES6 import declaration
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node represents a import declaration
+ */
+ function looksLikeImport(node) {
+ return node.type === "ImportDeclaration" || node.type === "ImportSpecifier" ||
+ node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier";
+ }
+
+ /**
+ * Checks whether a given node is a variable declaration or not.
+ *
+ * @param {ASTNode} node - any node
+ * @returns {boolean} `true` if the node is a variable declaration.
+ */
+ function isVariableDeclaration(node) {
+ return (
+ node.type === "VariableDeclaration" ||
+ (
+ node.type === "ExportNamedDeclaration" &&
+ node.declaration &&
+ node.declaration.type === "VariableDeclaration"
+ )
+ );
+ }
+
+ /**
+ * Checks whether this variable is on top of the block body
+ * @param {ASTNode} node - The node to check
+ * @param {ASTNode[]} statements - collection of ASTNodes for the parent node block
+ * @returns {boolean} True if var is on top otherwise false
+ */
+ function isVarOnTop(node, statements) {
+ const l = statements.length;
+ let i = 0;
+
+ // skip over directives
+ for (; i < l; ++i) {
+ if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
+ break;
+ }
+ }
+
+ for (; i < l; ++i) {
+ if (!isVariableDeclaration(statements[i])) {
+ return false;
+ }
+ if (statements[i] === node) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether variable is on top at the global level
+ * @param {ASTNode} node - The node to check
+ * @param {ASTNode} parent - Parent of the node
+ * @returns {void}
+ */
+ function globalVarCheck(node, parent) {
+ if (!isVarOnTop(node, parent.body)) {
+ context.report({ node, message: errorMessage });
+ }
+ }
+
+ /**
+ * Checks whether variable is on top at functional block scope level
+ * @param {ASTNode} node - The node to check
+ * @param {ASTNode} parent - Parent of the node
+ * @param {ASTNode} grandParent - Parent of the node's parent
+ * @returns {void}
+ */
+ function blockScopeVarCheck(node, parent, grandParent) {
+ if (!(/Function/.test(grandParent.type) &&
+ parent.type === "BlockStatement" &&
+ isVarOnTop(node, parent.body))) {
+ context.report({ node, message: errorMessage });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+ VariableDeclaration(node) {
+ const ancestors = context.getAncestors();
+ let parent = ancestors.pop();
+ let grandParent = ancestors.pop();
+
+ if (node.kind === "var") { // check variable is `var` type and not `let` or `const`
+ if (parent.type === "ExportNamedDeclaration") {
+ node = parent;
+ parent = grandParent;
+ grandParent = ancestors.pop();
+ }
+
+ if (parent.type === "Program") { // That means its a global variable
+ globalVarCheck(node, parent);
+ } else {
+ blockScopeVarCheck(node, parent, grandParent);
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/wrap-iife.js b/tools/node_modules/eslint/lib/rules/wrap-iife.js
new file mode 100644
index 0000000000..c4e6a9e0c7
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/wrap-iife.js
@@ -0,0 +1,151 @@
+/**
+ * @fileoverview Rule to flag when IIFE is not wrapped in parens
+ * @author Ilya Volodin
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require parentheses around immediate `function` invocations",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["outside", "inside", "any"]
+ },
+ {
+ type: "object",
+ properties: {
+ functionPrototypeMethods: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const style = context.options[0] || "outside";
+ const includeFunctionPrototypeMethods = (context.options[1] && context.options[1].functionPrototypeMethods) || false;
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Check if the node is wrapped in ()
+ * @param {ASTNode} node node to evaluate
+ * @returns {boolean} True if it is wrapped
+ * @private
+ */
+ function wrapped(node) {
+ return astUtils.isParenthesised(sourceCode, node);
+ }
+
+ /**
+ * Get the function node from an IIFE
+ * @param {ASTNode} node node to evaluate
+ * @returns {ASTNode} node that is the function expression of the given IIFE, or null if none exist
+ */
+ function getFunctionNodeFromIIFE(node) {
+ const callee = node.callee;
+
+ if (callee.type === "FunctionExpression") {
+ return callee;
+ }
+
+ if (includeFunctionPrototypeMethods &&
+ callee.type === "MemberExpression" &&
+ callee.object.type === "FunctionExpression" &&
+ (astUtils.getStaticPropertyName(callee) === "call" || astUtils.getStaticPropertyName(callee) === "apply")
+ ) {
+ return callee.object;
+ }
+
+ return null;
+ }
+
+
+ return {
+ CallExpression(node) {
+ const innerNode = getFunctionNodeFromIIFE(node);
+
+ if (!innerNode) {
+ return;
+ }
+
+ const callExpressionWrapped = wrapped(node),
+ functionExpressionWrapped = wrapped(innerNode);
+
+ if (!callExpressionWrapped && !functionExpressionWrapped) {
+ context.report({
+ node,
+ message: "Wrap an immediate function invocation in parentheses.",
+ fix(fixer) {
+ const nodeToSurround = style === "inside" ? innerNode : node;
+
+ return fixer.replaceText(nodeToSurround, `(${sourceCode.getText(nodeToSurround)})`);
+ }
+ });
+ } else if (style === "inside" && !functionExpressionWrapped) {
+ context.report({
+ node,
+ message: "Wrap only the function expression in parens.",
+ fix(fixer) {
+
+ /*
+ * The outer call expression will always be wrapped at this point.
+ * Replace the range between the end of the function expression and the end of the call expression.
+ * for example, in `(function(foo) {}(bar))`, the range `(bar))` should get replaced with `)(bar)`.
+ * Replace the parens from the outer expression, and parenthesize the function expression.
+ */
+ const parenAfter = sourceCode.getTokenAfter(node);
+
+ return fixer.replaceTextRange(
+ [innerNode.range[1], parenAfter.range[1]],
+ `)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}`
+ );
+ }
+ });
+ } else if (style === "outside" && !callExpressionWrapped) {
+ context.report({
+ node,
+ message: "Move the invocation into the parens that contain the function.",
+ fix(fixer) {
+
+ /*
+ * The inner function expression will always be wrapped at this point.
+ * It's only necessary to replace the range between the end of the function expression
+ * and the call expression. For example, in `(function(foo) {})(bar)`, the range `)(bar)`
+ * should get replaced with `(bar))`.
+ */
+ const parenAfter = sourceCode.getTokenAfter(innerNode);
+
+ return fixer.replaceTextRange(
+ [parenAfter.range[0], node.range[1]],
+ `${sourceCode.getText().slice(parenAfter.range[1], node.range[1])})`
+ );
+ }
+ });
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/wrap-regex.js b/tools/node_modules/eslint/lib/rules/wrap-regex.js
new file mode 100644
index 0000000000..79f3d30515
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/wrap-regex.js
@@ -0,0 +1,52 @@
+/**
+ * @fileoverview Rule to flag when regex literals are not wrapped in parens
+ * @author Matt DuVall <http://www.mattduvall.com>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require parenthesis around regex literals",
+ category: "Stylistic Issues",
+ recommended: false
+ },
+
+ schema: [],
+
+ fixable: "code"
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ return {
+
+ Literal(node) {
+ const token = sourceCode.getFirstToken(node),
+ nodeType = token.type;
+
+ if (nodeType === "RegularExpression") {
+ const source = sourceCode.getTokenBefore(node);
+ const ancestors = context.getAncestors();
+ const grandparent = ancestors[ancestors.length - 1];
+
+ if (grandparent.type === "MemberExpression" && grandparent.object === node &&
+ (!source || source.value !== "(")) {
+ context.report({
+ node,
+ message: "Wrap the regexp literal in parens to disambiguate the slash.",
+ fix: fixer => fixer.replaceText(node, `(${sourceCode.getText(node)})`)
+ });
+ }
+ }
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/yield-star-spacing.js b/tools/node_modules/eslint/lib/rules/yield-star-spacing.js
new file mode 100644
index 0000000000..eb20fc01b0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/yield-star-spacing.js
@@ -0,0 +1,117 @@
+/**
+ * @fileoverview Rule to check the spacing around the * in yield* expressions.
+ * @author Bryan Smith
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow spacing around the `*` in `yield*` expressions",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ fixable: "whitespace",
+
+ schema: [
+ {
+ oneOf: [
+ {
+ enum: ["before", "after", "both", "neither"]
+ },
+ {
+ type: "object",
+ properties: {
+ before: { type: "boolean" },
+ after: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ ]
+ },
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+
+ const mode = (function(option) {
+ if (!option || typeof option === "string") {
+ return {
+ before: { before: true, after: false },
+ after: { before: false, after: true },
+ both: { before: true, after: true },
+ neither: { before: false, after: false }
+ }[option || "after"];
+ }
+ return option;
+ }(context.options[0]));
+
+ /**
+ * Checks the spacing between two tokens before or after the star token.
+ * @param {string} side Either "before" or "after".
+ * @param {Token} leftToken `function` keyword token if side is "before", or
+ * star token if side is "after".
+ * @param {Token} rightToken Star token if side is "before", or identifier
+ * token if side is "after".
+ * @returns {void}
+ */
+ function checkSpacing(side, leftToken, rightToken) {
+ if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken) !== mode[side]) {
+ const after = leftToken.value === "*";
+ const spaceRequired = mode[side];
+ const node = after ? leftToken : rightToken;
+ const type = spaceRequired ? "Missing" : "Unexpected";
+ const message = "{{type}} space {{side}} *.";
+
+ context.report({
+ node,
+ message,
+ data: {
+ type,
+ side
+ },
+ fix(fixer) {
+ if (spaceRequired) {
+ if (after) {
+ return fixer.insertTextAfter(node, " ");
+ }
+ return fixer.insertTextBefore(node, " ");
+ }
+ return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
+ }
+ });
+ }
+ }
+
+ /**
+ * Enforces the spacing around the star if node is a yield* expression.
+ * @param {ASTNode} node A yield expression node.
+ * @returns {void}
+ */
+ function checkExpression(node) {
+ if (!node.delegate) {
+ return;
+ }
+
+ const tokens = sourceCode.getFirstTokens(node, 3);
+ const yieldToken = tokens[0];
+ const starToken = tokens[1];
+ const nextToken = tokens[2];
+
+ checkSpacing("before", yieldToken, starToken);
+ checkSpacing("after", starToken, nextToken);
+ }
+
+ return {
+ YieldExpression: checkExpression
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/rules/yoda.js b/tools/node_modules/eslint/lib/rules/yoda.js
new file mode 100644
index 0000000000..2ccb61f73a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/yoda.js
@@ -0,0 +1,310 @@
+/**
+ * @fileoverview Rule to require or disallow yoda comparisons
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//--------------------------------------------------------------------------
+// Requirements
+//--------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//--------------------------------------------------------------------------
+// Helpers
+//--------------------------------------------------------------------------
+
+/**
+ * Determines whether an operator is a comparison operator.
+ * @param {string} operator The operator to check.
+ * @returns {boolean} Whether or not it is a comparison operator.
+ */
+function isComparisonOperator(operator) {
+ return (/^(==|===|!=|!==|<|>|<=|>=)$/).test(operator);
+}
+
+/**
+ * Determines whether an operator is an equality operator.
+ * @param {string} operator The operator to check.
+ * @returns {boolean} Whether or not it is an equality operator.
+ */
+function isEqualityOperator(operator) {
+ return (/^(==|===)$/).test(operator);
+}
+
+/**
+ * Determines whether an operator is one used in a range test.
+ * Allowed operators are `<` and `<=`.
+ * @param {string} operator The operator to check.
+ * @returns {boolean} Whether the operator is used in range tests.
+ */
+function isRangeTestOperator(operator) {
+ return ["<", "<="].indexOf(operator) >= 0;
+}
+
+/**
+ * Determines whether a non-Literal node is a negative number that should be
+ * treated as if it were a single Literal node.
+ * @param {ASTNode} node Node to test.
+ * @returns {boolean} True if the node is a negative number that looks like a
+ * real literal and should be treated as such.
+ */
+function looksLikeLiteral(node) {
+ return (node.type === "UnaryExpression" &&
+ node.operator === "-" &&
+ node.prefix &&
+ node.argument.type === "Literal" &&
+ typeof node.argument.value === "number");
+}
+
+/**
+ * Attempts to derive a Literal node from nodes that are treated like literals.
+ * @param {ASTNode} node Node to normalize.
+ * @param {number} [defaultValue] The default value to be returned if the node
+ * is not a Literal.
+ * @returns {ASTNode} One of the following options.
+ * 1. The original node if the node is already a Literal
+ * 2. A normalized Literal node with the negative number as the value if the
+ * node represents a negative number literal.
+ * 3. The Literal node which has the `defaultValue` argument if it exists.
+ * 4. Otherwise `null`.
+ */
+function getNormalizedLiteral(node, defaultValue) {
+ if (node.type === "Literal") {
+ return node;
+ }
+
+ if (looksLikeLiteral(node)) {
+ return {
+ type: "Literal",
+ value: -node.argument.value,
+ raw: `-${node.argument.value}`
+ };
+ }
+
+ if (defaultValue) {
+ return {
+ type: "Literal",
+ value: defaultValue,
+ raw: String(defaultValue)
+ };
+ }
+
+ return null;
+}
+
+/**
+ * Checks whether two expressions reference the same value. For example:
+ * a = a
+ * a.b = a.b
+ * a[0] = a[0]
+ * a['b'] = a['b']
+ * @param {ASTNode} a Left side of the comparison.
+ * @param {ASTNode} b Right side of the comparison.
+ * @returns {boolean} True if both sides match and reference the same value.
+ */
+function same(a, b) {
+ if (a.type !== b.type) {
+ return false;
+ }
+
+ switch (a.type) {
+ case "Identifier":
+ return a.name === b.name;
+
+ case "Literal":
+ return a.value === b.value;
+
+ case "MemberExpression": {
+ const nameA = astUtils.getStaticPropertyName(a);
+
+ // x.y = x["y"]
+ if (nameA) {
+ return (
+ same(a.object, b.object) &&
+ nameA === astUtils.getStaticPropertyName(b)
+ );
+ }
+
+ /*
+ * x[0] = x[0]
+ * x[y] = x[y]
+ * x.y = x.y
+ */
+ return (
+ a.computed === b.computed &&
+ same(a.object, b.object) &&
+ same(a.property, b.property)
+ );
+ }
+
+ case "ThisExpression":
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "require or disallow \"Yoda\" conditions",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ exceptRange: {
+ type: "boolean"
+ },
+ onlyEquality: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ // Default to "never" (!always) if no option
+ const always = (context.options[0] === "always");
+ const exceptRange = (context.options[1] && context.options[1].exceptRange);
+ const onlyEquality = (context.options[1] && context.options[1].onlyEquality);
+
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether node represents a range test.
+ * A range test is a "between" test like `(0 <= x && x < 1)` or an "outside"
+ * test like `(x < 0 || 1 <= x)`. It must be wrapped in parentheses, and
+ * both operators must be `<` or `<=`. Finally, the literal on the left side
+ * must be less than or equal to the literal on the right side so that the
+ * test makes any sense.
+ * @param {ASTNode} node LogicalExpression node to test.
+ * @returns {boolean} Whether node is a range test.
+ */
+ function isRangeTest(node) {
+ const left = node.left,
+ right = node.right;
+
+ /**
+ * Determines whether node is of the form `0 <= x && x < 1`.
+ * @returns {boolean} Whether node is a "between" range test.
+ */
+ function isBetweenTest() {
+ let leftLiteral, rightLiteral;
+
+ return (node.operator === "&&" &&
+ (leftLiteral = getNormalizedLiteral(left.left)) &&
+ (rightLiteral = getNormalizedLiteral(right.right, Number.POSITIVE_INFINITY)) &&
+ leftLiteral.value <= rightLiteral.value &&
+ same(left.right, right.left));
+ }
+
+ /**
+ * Determines whether node is of the form `x < 0 || 1 <= x`.
+ * @returns {boolean} Whether node is an "outside" range test.
+ */
+ function isOutsideTest() {
+ let leftLiteral, rightLiteral;
+
+ return (node.operator === "||" &&
+ (leftLiteral = getNormalizedLiteral(left.right, Number.NEGATIVE_INFINITY)) &&
+ (rightLiteral = getNormalizedLiteral(right.left)) &&
+ leftLiteral.value <= rightLiteral.value &&
+ same(left.left, right.right));
+ }
+
+ /**
+ * Determines whether node is wrapped in parentheses.
+ * @returns {boolean} Whether node is preceded immediately by an open
+ * paren token and followed immediately by a close
+ * paren token.
+ */
+ function isParenWrapped() {
+ return astUtils.isParenthesised(sourceCode, node);
+ }
+
+ return (node.type === "LogicalExpression" &&
+ left.type === "BinaryExpression" &&
+ right.type === "BinaryExpression" &&
+ isRangeTestOperator(left.operator) &&
+ isRangeTestOperator(right.operator) &&
+ (isBetweenTest() || isOutsideTest()) &&
+ isParenWrapped());
+ }
+
+ const OPERATOR_FLIP_MAP = {
+ "===": "===",
+ "!==": "!==",
+ "==": "==",
+ "!=": "!=",
+ "<": ">",
+ ">": "<",
+ "<=": ">=",
+ ">=": "<="
+ };
+
+ /**
+ * Returns a string representation of a BinaryExpression node with its sides/operator flipped around.
+ * @param {ASTNode} node The BinaryExpression node
+ * @returns {string} A string representation of the node with the sides and operator flipped
+ */
+ function getFlippedString(node) {
+ const operatorToken = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
+ const textBeforeOperator = sourceCode.getText().slice(sourceCode.getTokenBefore(operatorToken).range[1], operatorToken.range[0]);
+ const textAfterOperator = sourceCode.getText().slice(operatorToken.range[1], sourceCode.getTokenAfter(operatorToken).range[0]);
+ const leftText = sourceCode.getText().slice(node.range[0], sourceCode.getTokenBefore(operatorToken).range[1]);
+ const rightText = sourceCode.getText().slice(sourceCode.getTokenAfter(operatorToken).range[0], node.range[1]);
+
+ return rightText + textBeforeOperator + OPERATOR_FLIP_MAP[operatorToken.value] + textAfterOperator + leftText;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ return {
+ BinaryExpression(node) {
+ const expectedLiteral = always ? node.left : node.right;
+ const expectedNonLiteral = always ? node.right : node.left;
+
+ // If `expectedLiteral` is not a literal, and `expectedNonLiteral` is a literal, raise an error.
+ if (
+ (expectedNonLiteral.type === "Literal" || looksLikeLiteral(expectedNonLiteral)) &&
+ !(expectedLiteral.type === "Literal" || looksLikeLiteral(expectedLiteral)) &&
+ !(!isEqualityOperator(node.operator) && onlyEquality) &&
+ isComparisonOperator(node.operator) &&
+ !(exceptRange && isRangeTest(context.getAncestors().pop()))
+ ) {
+ context.report({
+ node,
+ message: "Expected literal to be on the {{expectedSide}} side of {{operator}}.",
+ data: {
+ operator: node.operator,
+ expectedSide: always ? "left" : "right"
+ },
+ fix: fixer => fixer.replaceText(node, getFlippedString(node))
+ });
+ }
+
+ }
+ };
+
+ }
+};
diff --git a/tools/node_modules/eslint/lib/testers/rule-tester.js b/tools/node_modules/eslint/lib/testers/rule-tester.js
new file mode 100644
index 0000000000..aae66301d8
--- /dev/null
+++ b/tools/node_modules/eslint/lib/testers/rule-tester.js
@@ -0,0 +1,558 @@
+/**
+ * @fileoverview Mocha test wrapper
+ * @author Ilya Volodin
+ */
+"use strict";
+
+/* global describe, it */
+
+/*
+ * This is a wrapper around mocha to allow for DRY unittests for eslint
+ * Format:
+ * RuleTester.run("{ruleName}", {
+ * valid: [
+ * "{code}",
+ * { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings} }
+ * ],
+ * invalid: [
+ * { code: "{code}", errors: {numErrors} },
+ * { code: "{code}", errors: ["{errorMessage}"] },
+ * { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings}, errors: [{ message: "{errorMessage}", type: "{errorNodeType}"}] }
+ * ]
+ * });
+ *
+ * Variables:
+ * {code} - String that represents the code to be tested
+ * {options} - Arguments that are passed to the configurable rules.
+ * {globals} - An object representing a list of variables that are
+ * registered as globals
+ * {parser} - String representing the parser to use
+ * {settings} - An object representing global settings for all rules
+ * {numErrors} - If failing case doesn't need to check error message,
+ * this integer will specify how many errors should be
+ * received
+ * {errorMessage} - Message that is returned by the rule on failure
+ * {errorNodeType} - AST node type that is returned by they rule as
+ * a cause of the failure.
+ */
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash"),
+ assert = require("assert"),
+ util = require("util"),
+ validator = require("../config/config-validator"),
+ ajv = require("../util/ajv"),
+ Linter = require("../linter"),
+ Environments = require("../config/environments"),
+ SourceCodeFixer = require("../util/source-code-fixer");
+
+//------------------------------------------------------------------------------
+// Private Members
+//------------------------------------------------------------------------------
+
+/*
+ * testerDefaultConfig must not be modified as it allows to reset the tester to
+ * the initial default configuration
+ */
+const testerDefaultConfig = { rules: {} };
+let defaultConfig = { rules: {} };
+
+/*
+ * List every parameters possible on a test case that are not related to eslint
+ * configuration
+ */
+const RuleTesterParameters = [
+ "code",
+ "filename",
+ "options",
+ "errors",
+ "output"
+];
+
+const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
+
+/**
+ * Clones a given value deeply.
+ * Note: This ignores `parent` property.
+ *
+ * @param {any} x - A value to clone.
+ * @returns {any} A cloned value.
+ */
+function cloneDeeplyExcludesParent(x) {
+ if (typeof x === "object" && x !== null) {
+ if (Array.isArray(x)) {
+ return x.map(cloneDeeplyExcludesParent);
+ }
+
+ const retv = {};
+
+ for (const key in x) {
+ if (key !== "parent" && hasOwnProperty(x, key)) {
+ retv[key] = cloneDeeplyExcludesParent(x[key]);
+ }
+ }
+
+ return retv;
+ }
+
+ return x;
+}
+
+/**
+ * Freezes a given value deeply.
+ *
+ * @param {any} x - A value to freeze.
+ * @returns {void}
+ */
+function freezeDeeply(x) {
+ if (typeof x === "object" && x !== null) {
+ if (Array.isArray(x)) {
+ x.forEach(freezeDeeply);
+ } else {
+ for (const key in x) {
+ if (key !== "parent" && hasOwnProperty(x, key)) {
+ freezeDeeply(x[key]);
+ }
+ }
+ }
+ Object.freeze(x);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+// default separators for testing
+const DESCRIBE = Symbol("describe");
+const IT = Symbol("it");
+
+/**
+ * This is `it` or `describe` if those don't exist.
+ * @this {Mocha}
+ * @param {string} text - The description of the test case.
+ * @param {Function} method - The logic of the test case.
+ * @returns {any} Returned value of `method`.
+ */
+function defaultHandler(text, method) {
+ return method.apply(this);
+}
+
+class RuleTester {
+
+ /**
+ * Creates a new instance of RuleTester.
+ * @param {Object} [testerConfig] Optional, extra configuration for the tester
+ * @constructor
+ */
+ constructor(testerConfig) {
+
+ /**
+ * The configuration to use for this tester. Combination of the tester
+ * configuration and the default configuration.
+ * @type {Object}
+ */
+ this.testerConfig = lodash.merge(
+
+ // we have to clone because merge uses the first argument for recipient
+ lodash.cloneDeep(defaultConfig),
+ testerConfig,
+ { rules: { "rule-tester/validate-ast": "error" } }
+ );
+
+ /**
+ * Rule definitions to define before tests.
+ * @type {Object}
+ */
+ this.rules = {};
+ this.linter = new Linter();
+ }
+
+ /**
+ * Set the configuration to use for all future tests
+ * @param {Object} config the configuration to use.
+ * @returns {void}
+ */
+ static setDefaultConfig(config) {
+ if (typeof config !== "object") {
+ throw new TypeError("RuleTester.setDefaultConfig: config must be an object");
+ }
+ defaultConfig = config;
+
+ // Make sure the rules object exists since it is assumed to exist later
+ defaultConfig.rules = defaultConfig.rules || {};
+ }
+
+ /**
+ * Get the current configuration used for all tests
+ * @returns {Object} the current configuration
+ */
+ static getDefaultConfig() {
+ return defaultConfig;
+ }
+
+ /**
+ * Reset the configuration to the initial configuration of the tester removing
+ * any changes made until now.
+ * @returns {void}
+ */
+ static resetDefaultConfig() {
+ defaultConfig = lodash.cloneDeep(testerDefaultConfig);
+ }
+
+
+ /*
+ * If people use `mocha test.js --watch` command, `describe` and `it` function
+ * instances are different for each execution. So `describe` and `it` should get fresh instance
+ * always.
+ */
+ static get describe() {
+ return (
+ this[DESCRIBE] ||
+ (typeof describe === "function" ? describe : defaultHandler)
+ );
+ }
+
+ static set describe(value) {
+ this[DESCRIBE] = value;
+ }
+
+ static get it() {
+ return (
+ this[IT] ||
+ (typeof it === "function" ? it : defaultHandler)
+ );
+ }
+
+ static set it(value) {
+ this[IT] = value;
+ }
+
+ /**
+ * Define a rule for one particular run of tests.
+ * @param {string} name The name of the rule to define.
+ * @param {Function} rule The rule definition.
+ * @returns {void}
+ */
+ defineRule(name, rule) {
+ this.rules[name] = rule;
+ }
+
+ /**
+ * Adds a new rule test to execute.
+ * @param {string} ruleName The name of the rule to run.
+ * @param {Function} rule The rule to test.
+ * @param {Object} test The collection of tests to run.
+ * @returns {void}
+ */
+ run(ruleName, rule, test) {
+
+ const testerConfig = this.testerConfig,
+ requiredScenarios = ["valid", "invalid"],
+ scenarioErrors = [],
+ linter = this.linter;
+
+ if (lodash.isNil(test) || typeof test !== "object") {
+ throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
+ }
+
+ requiredScenarios.forEach(scenarioType => {
+ if (lodash.isNil(test[scenarioType])) {
+ scenarioErrors.push(`Could not find any ${scenarioType} test scenarios`);
+ }
+ });
+
+ if (scenarioErrors.length > 0) {
+ throw new Error([
+ `Test Scenarios for rule ${ruleName} is invalid:`
+ ].concat(scenarioErrors).join("\n"));
+ }
+
+
+ linter.defineRule(ruleName, Object.assign({}, rule, {
+
+ // Create a wrapper rule that freezes the `context` properties.
+ create(context) {
+ freezeDeeply(context.options);
+ freezeDeeply(context.settings);
+ freezeDeeply(context.parserOptions);
+
+ return (typeof rule === "function" ? rule : rule.create)(context);
+ }
+ }));
+
+ linter.defineRules(this.rules);
+
+ const ruleMap = linter.getRules();
+
+ /**
+ * Run the rule for the given item
+ * @param {string|Object} item Item to run the rule against
+ * @returns {Object} Eslint run result
+ * @private
+ */
+ function runRuleForItem(item) {
+ let config = lodash.cloneDeep(testerConfig),
+ code, filename, beforeAST, afterAST;
+
+ if (typeof item === "string") {
+ code = item;
+ } else {
+ code = item.code;
+
+ /*
+ * Assumes everything on the item is a config except for the
+ * parameters used by this tester
+ */
+ const itemConfig = lodash.omit(item, RuleTesterParameters);
+
+ /*
+ * Create the config object from the tester config and this item
+ * specific configurations.
+ */
+ config = lodash.merge(
+ config,
+ itemConfig
+ );
+ }
+
+ if (item.filename) {
+ filename = item.filename;
+ }
+
+ if (Object.prototype.hasOwnProperty.call(item, "options")) {
+ assert(Array.isArray(item.options), "options must be an array");
+ config.rules[ruleName] = [1].concat(item.options);
+ } else {
+ config.rules[ruleName] = 1;
+ }
+
+ const schema = validator.getRuleOptionsSchema(rule);
+
+ /*
+ * Setup AST getters.
+ * The goal is to check whether or not AST was modified when
+ * running the rule under test.
+ */
+ linter.defineRule("rule-tester/validate-ast", () => ({
+ Program(node) {
+ beforeAST = cloneDeeplyExcludesParent(node);
+ },
+ "Program:exit"(node) {
+ afterAST = node;
+ }
+ }));
+
+ if (schema) {
+ ajv.validateSchema(schema);
+
+ if (ajv.errors) {
+ const errors = ajv.errors.map(error => {
+ const field = error.dataPath[0] === "." ? error.dataPath.slice(1) : error.dataPath;
+
+ return `\t${field}: ${error.message}`;
+ }).join("\n");
+
+ throw new Error([`Schema for rule ${ruleName} is invalid:`, errors]);
+ }
+ }
+
+ validator.validate(config, "rule-tester", ruleMap.get.bind(ruleMap), new Environments());
+
+ return {
+ messages: linter.verify(code, config, filename, true),
+ beforeAST,
+ afterAST: cloneDeeplyExcludesParent(afterAST)
+ };
+ }
+
+ /**
+ * Check if the AST was changed
+ * @param {ASTNode} beforeAST AST node before running
+ * @param {ASTNode} afterAST AST node after running
+ * @returns {void}
+ * @private
+ */
+ function assertASTDidntChange(beforeAST, afterAST) {
+ if (!lodash.isEqual(beforeAST, afterAST)) {
+
+ // Not using directly to avoid performance problem in node 6.1.0. See #6111
+ // eslint-disable-next-line no-restricted-properties
+ assert.deepEqual(beforeAST, afterAST, "Rule should not modify AST.");
+ }
+ }
+
+ /**
+ * Check if the template is valid or not
+ * all valid cases go through this
+ * @param {string|Object} item Item to run the rule against
+ * @returns {void}
+ * @private
+ */
+ function testValidTemplate(item) {
+ const result = runRuleForItem(item);
+ const messages = result.messages;
+
+ assert.strictEqual(messages.length, 0, util.format("Should have no errors but had %d: %s",
+ messages.length, util.inspect(messages)));
+
+ assertASTDidntChange(result.beforeAST, result.afterAST);
+ }
+
+ /**
+ * Asserts that the message matches its expected value. If the expected
+ * value is a regular expression, it is checked against the actual
+ * value.
+ * @param {string} actual Actual value
+ * @param {string|RegExp} expected Expected value
+ * @returns {void}
+ * @private
+ */
+ function assertMessageMatches(actual, expected) {
+ if (expected instanceof RegExp) {
+
+ // assert.js doesn't have a built-in RegExp match function
+ assert.ok(
+ expected.test(actual),
+ `Expected '${actual}' to match ${expected}`
+ );
+ } else {
+ assert.strictEqual(actual, expected);
+ }
+ }
+
+ /**
+ * Check if the template is invalid or not
+ * all invalid cases go through this.
+ * @param {string|Object} item Item to run the rule against
+ * @returns {void}
+ * @private
+ */
+ function testInvalidTemplate(item) {
+ assert.ok(item.errors || item.errors === 0,
+ `Did not specify errors for an invalid test of ${ruleName}`);
+
+ const result = runRuleForItem(item);
+ const messages = result.messages;
+
+
+ if (typeof item.errors === "number") {
+ assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
+ item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
+ } else {
+ assert.strictEqual(
+ messages.length, item.errors.length,
+ util.format(
+ "Should have %d error%s but had %d: %s",
+ item.errors.length, item.errors.length === 1 ? "" : "s", messages.length, util.inspect(messages)
+ )
+ );
+
+ const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleName);
+
+ for (let i = 0, l = item.errors.length; i < l; i++) {
+ assert(!messages[i].fatal, `A fatal parsing error occurred: ${messages[i].message}`);
+ assert(hasMessageOfThisRule, "Error rule name should be the same as the name of the rule being tested");
+
+ if (typeof item.errors[i] === "string" || item.errors[i] instanceof RegExp) {
+
+ // Just an error message.
+ assertMessageMatches(messages[i].message, item.errors[i]);
+ } else if (typeof item.errors[i] === "object") {
+
+ /*
+ * Error object.
+ * This may have a message, node type, line, and/or
+ * column.
+ */
+ if (item.errors[i].message) {
+ assertMessageMatches(messages[i].message, item.errors[i].message);
+ }
+
+ // The following checks use loose equality assertions for backwards compatibility.
+
+ if (item.errors[i].type) {
+
+ // eslint-disable-next-line no-restricted-properties
+ assert.equal(messages[i].nodeType, item.errors[i].type, `Error type should be ${item.errors[i].type}, found ${messages[i].nodeType}`);
+ }
+
+ if (item.errors[i].hasOwnProperty("line")) {
+
+ // eslint-disable-next-line no-restricted-properties
+ assert.equal(messages[i].line, item.errors[i].line, `Error line should be ${item.errors[i].line}`);
+ }
+
+ if (item.errors[i].hasOwnProperty("column")) {
+
+ // eslint-disable-next-line no-restricted-properties
+ assert.equal(messages[i].column, item.errors[i].column, `Error column should be ${item.errors[i].column}`);
+ }
+
+ if (item.errors[i].hasOwnProperty("endLine")) {
+
+ // eslint-disable-next-line no-restricted-properties
+ assert.equal(messages[i].endLine, item.errors[i].endLine, `Error endLine should be ${item.errors[i].endLine}`);
+ }
+
+ if (item.errors[i].hasOwnProperty("endColumn")) {
+
+ // eslint-disable-next-line no-restricted-properties
+ assert.equal(messages[i].endColumn, item.errors[i].endColumn, `Error endColumn should be ${item.errors[i].endColumn}`);
+ }
+ } else {
+
+ // Message was an unexpected type
+ assert.fail(messages[i], null, "Error should be a string, object, or RegExp.");
+ }
+ }
+ }
+
+ if (item.hasOwnProperty("output")) {
+ if (item.output === null) {
+ assert.strictEqual(
+ messages.filter(message => message.fix).length,
+ 0,
+ "Expected no autofixes to be suggested"
+ );
+ } else {
+ const fixResult = SourceCodeFixer.applyFixes(item.code, messages);
+
+ // eslint-disable-next-line no-restricted-properties
+ assert.equal(fixResult.output, item.output, "Output is incorrect.");
+ }
+ }
+
+ assertASTDidntChange(result.beforeAST, result.afterAST);
+ }
+
+ /*
+ * This creates a mocha test suite and pipes all supplied info through
+ * one of the templates above.
+ */
+ RuleTester.describe(ruleName, () => {
+ RuleTester.describe("valid", () => {
+ test.valid.forEach(valid => {
+ RuleTester.it(typeof valid === "object" ? valid.code : valid, () => {
+ testValidTemplate(valid);
+ });
+ });
+ });
+
+ RuleTester.describe("invalid", () => {
+ test.invalid.forEach(invalid => {
+ RuleTester.it(invalid.code, () => {
+ testInvalidTemplate(invalid);
+ });
+ });
+ });
+ });
+ }
+}
+
+RuleTester[DESCRIBE] = RuleTester[IT] = null;
+
+module.exports = RuleTester;
diff --git a/tools/node_modules/eslint/lib/timing.js b/tools/node_modules/eslint/lib/timing.js
new file mode 100644
index 0000000000..e33ac8f458
--- /dev/null
+++ b/tools/node_modules/eslint/lib/timing.js
@@ -0,0 +1,141 @@
+/**
+ * @fileoverview Tracks performance of individual rules.
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/* istanbul ignore next */
+/**
+ * Align the string to left
+ * @param {string} str string to evaluate
+ * @param {int} len length of the string
+ * @param {string} ch delimiter character
+ * @returns {string} modified string
+ * @private
+ */
+function alignLeft(str, len, ch) {
+ return str + new Array(len - str.length + 1).join(ch || " ");
+}
+
+/* istanbul ignore next */
+/**
+ * Align the string to right
+ * @param {string} str string to evaluate
+ * @param {int} len length of the string
+ * @param {string} ch delimiter character
+ * @returns {string} modified string
+ * @private
+ */
+function alignRight(str, len, ch) {
+ return new Array(len - str.length + 1).join(ch || " ") + str;
+}
+
+//------------------------------------------------------------------------------
+// Module definition
+//------------------------------------------------------------------------------
+
+const enabled = !!process.env.TIMING;
+
+const HEADERS = ["Rule", "Time (ms)", "Relative"];
+const ALIGN = [alignLeft, alignRight, alignRight];
+
+/* istanbul ignore next */
+/**
+ * display the data
+ * @param {Object} data Data object to be displayed
+ * @returns {string} modified string
+ * @private
+ */
+function display(data) {
+ let total = 0;
+ const rows = Object.keys(data)
+ .map(key => {
+ const time = data[key];
+
+ total += time;
+ return [key, time];
+ })
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, 10);
+
+ rows.forEach(row => {
+ row.push(`${(row[1] * 100 / total).toFixed(1)}%`);
+ row[1] = row[1].toFixed(3);
+ });
+
+ rows.unshift(HEADERS);
+
+ const widths = [];
+
+ rows.forEach(row => {
+ const len = row.length;
+
+ for (let i = 0; i < len; i++) {
+ const n = row[i].length;
+
+ if (!widths[i] || n > widths[i]) {
+ widths[i] = n;
+ }
+ }
+ });
+
+ const table = rows.map(row => (
+ row
+ .map((cell, index) => ALIGN[index](cell, widths[index]))
+ .join(" | ")
+ ));
+
+ table.splice(1, 0, widths.map((w, index) => {
+ if (index !== 0 && index !== widths.length - 1) {
+ w++;
+ }
+
+ return ALIGN[index](":", w + 1, "-");
+ }).join("|"));
+
+ console.log(table.join("\n")); // eslint-disable-line no-console
+}
+
+/* istanbul ignore next */
+module.exports = (function() {
+
+ const data = Object.create(null);
+
+ /**
+ * Time the run
+ * @param {*} key key from the data object
+ * @param {Function} fn function to be called
+ * @returns {Function} function to be executed
+ * @private
+ */
+ function time(key, fn) {
+ if (typeof data[key] === "undefined") {
+ data[key] = 0;
+ }
+
+ return function() {
+ let t = process.hrtime();
+
+ fn.apply(null, Array.prototype.slice.call(arguments));
+ t = process.hrtime(t);
+ data[key] += t[0] * 1e3 + t[1] / 1e6;
+ };
+ }
+
+ if (enabled) {
+ process.on("exit", () => {
+ display(data);
+ });
+ }
+
+ return {
+ time,
+ enabled
+ };
+
+}());
diff --git a/tools/node_modules/eslint/lib/token-store/backward-token-comment-cursor.js b/tools/node_modules/eslint/lib/token-store/backward-token-comment-cursor.js
new file mode 100644
index 0000000000..7c2137a176
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/backward-token-comment-cursor.js
@@ -0,0 +1,57 @@
+/**
+ * @fileoverview Define the cursor which iterates tokens and comments in reverse.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Cursor = require("./cursor");
+const utils = require("./utils");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The cursor which iterates tokens and comments in reverse.
+ */
+module.exports = class BackwardTokenCommentCursor extends Cursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ */
+ constructor(tokens, comments, indexMap, startLoc, endLoc) {
+ super();
+ this.tokens = tokens;
+ this.comments = comments;
+ this.tokenIndex = utils.getLastIndex(tokens, indexMap, endLoc);
+ this.commentIndex = utils.search(comments, endLoc) - 1;
+ this.border = startLoc;
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ const token = (this.tokenIndex >= 0) ? this.tokens[this.tokenIndex] : null;
+ const comment = (this.commentIndex >= 0) ? this.comments[this.commentIndex] : null;
+
+ if (token && (!comment || token.range[1] > comment.range[1])) {
+ this.current = token;
+ this.tokenIndex -= 1;
+ } else if (comment) {
+ this.current = comment;
+ this.commentIndex -= 1;
+ } else {
+ this.current = null;
+ }
+
+ return Boolean(this.current) && (this.border === -1 || this.current.range[0] >= this.border);
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/backward-token-cursor.js b/tools/node_modules/eslint/lib/token-store/backward-token-cursor.js
new file mode 100644
index 0000000000..93973bce44
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/backward-token-cursor.js
@@ -0,0 +1,58 @@
+/**
+ * @fileoverview Define the cursor which iterates tokens only in reverse.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Cursor = require("./cursor");
+const utils = require("./utils");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The cursor which iterates tokens only in reverse.
+ */
+module.exports = class BackwardTokenCursor extends Cursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ */
+ constructor(tokens, comments, indexMap, startLoc, endLoc) {
+ super();
+ this.tokens = tokens;
+ this.index = utils.getLastIndex(tokens, indexMap, endLoc);
+ this.indexEnd = utils.getFirstIndex(tokens, indexMap, startLoc);
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ if (this.index >= this.indexEnd) {
+ this.current = this.tokens[this.index];
+ this.index -= 1;
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ *
+ * Shorthand for performance.
+ *
+ */
+
+ /** @inheritdoc */
+ getOneToken() {
+ return (this.index >= this.indexEnd) ? this.tokens[this.index] : null;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/cursor.js b/tools/node_modules/eslint/lib/token-store/cursor.js
new file mode 100644
index 0000000000..4e1595c6dc
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/cursor.js
@@ -0,0 +1,76 @@
+/**
+ * @fileoverview Define the abstract class about cursors which iterate tokens.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The abstract class about cursors which iterate tokens.
+ *
+ * This class has 2 abstract methods.
+ *
+ * - `current: Token | Comment | null` ... The current token.
+ * - `moveNext(): boolean` ... Moves this cursor to the next token. If the next token didn't exist, it returns `false`.
+ *
+ * This is similar to ES2015 Iterators.
+ * However, Iterators were slow (at 2017-01), so I created this class as similar to C# IEnumerable.
+ *
+ * There are the following known sub classes.
+ *
+ * - ForwardTokenCursor .......... The cursor which iterates tokens only.
+ * - BackwardTokenCursor ......... The cursor which iterates tokens only in reverse.
+ * - ForwardTokenCommentCursor ... The cursor which iterates tokens and comments.
+ * - BackwardTokenCommentCursor .. The cursor which iterates tokens and comments in reverse.
+ * - DecorativeCursor
+ * - FilterCursor ............ The cursor which ignores the specified tokens.
+ * - SkipCursor .............. The cursor which ignores the first few tokens.
+ * - LimitCursor ............. The cursor which limits the count of tokens.
+ *
+ */
+module.exports = class Cursor {
+
+ /**
+ * Initializes this cursor.
+ */
+ constructor() {
+ this.current = null;
+ }
+
+ /**
+ * Gets the first token.
+ * This consumes this cursor.
+ * @returns {Token|Comment} The first token or null.
+ */
+ getOneToken() {
+ return this.moveNext() ? this.current : null;
+ }
+
+ /**
+ * Gets the first tokens.
+ * This consumes this cursor.
+ * @returns {(Token|Comment)[]} All tokens.
+ */
+ getAllTokens() {
+ const tokens = [];
+
+ while (this.moveNext()) {
+ tokens.push(this.current);
+ }
+
+ return tokens;
+ }
+
+ /**
+ * Moves this cursor to the next token.
+ * @returns {boolean} `true` if the next token exists.
+ * @abstract
+ */
+ /* istanbul ignore next */
+ moveNext() { // eslint-disable-line class-methods-use-this
+ throw new Error("Not implemented.");
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/cursors.js b/tools/node_modules/eslint/lib/token-store/cursors.js
new file mode 100644
index 0000000000..b315c7e65e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/cursors.js
@@ -0,0 +1,92 @@
+/**
+ * @fileoverview Define 2 token factories; forward and backward.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const BackwardTokenCommentCursor = require("./backward-token-comment-cursor");
+const BackwardTokenCursor = require("./backward-token-cursor");
+const FilterCursor = require("./filter-cursor");
+const ForwardTokenCommentCursor = require("./forward-token-comment-cursor");
+const ForwardTokenCursor = require("./forward-token-cursor");
+const LimitCursor = require("./limit-cursor");
+const SkipCursor = require("./skip-cursor");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * The cursor factory.
+ * @private
+ */
+class CursorFactory {
+
+ /**
+ * Initializes this cursor.
+ * @param {Function} TokenCursor - The class of the cursor which iterates tokens only.
+ * @param {Function} TokenCommentCursor - The class of the cursor which iterates the mix of tokens and comments.
+ */
+ constructor(TokenCursor, TokenCommentCursor) {
+ this.TokenCursor = TokenCursor;
+ this.TokenCommentCursor = TokenCommentCursor;
+ }
+
+ /**
+ * Creates a base cursor instance that can be decorated by createCursor.
+ *
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {boolean} includeComments - The flag to iterate comments as well.
+ * @returns {Cursor} The created base cursor.
+ */
+ createBaseCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments) {
+ const Cursor = includeComments ? this.TokenCommentCursor : this.TokenCursor;
+
+ return new Cursor(tokens, comments, indexMap, startLoc, endLoc);
+ }
+
+ /**
+ * Creates a cursor that iterates tokens with normalized options.
+ *
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {boolean} includeComments - The flag to iterate comments as well.
+ * @param {Function|null} filter - The predicate function to choose tokens.
+ * @param {number} skip - The count of tokens the cursor skips.
+ * @param {number} count - The maximum count of tokens the cursor iterates. Zero is no iteration for backward compatibility.
+ * @returns {Cursor} The created cursor.
+ */
+ createCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments, filter, skip, count) {
+ let cursor = this.createBaseCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments);
+
+ if (filter) {
+ cursor = new FilterCursor(cursor, filter);
+ }
+ if (skip >= 1) {
+ cursor = new SkipCursor(cursor, skip);
+ }
+ if (count >= 0) {
+ cursor = new LimitCursor(cursor, count);
+ }
+
+ return cursor;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+exports.forward = new CursorFactory(ForwardTokenCursor, ForwardTokenCommentCursor);
+exports.backward = new CursorFactory(BackwardTokenCursor, BackwardTokenCommentCursor);
diff --git a/tools/node_modules/eslint/lib/token-store/decorative-cursor.js b/tools/node_modules/eslint/lib/token-store/decorative-cursor.js
new file mode 100644
index 0000000000..f0bff9c51d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/decorative-cursor.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Define the abstract class about cursors which manipulate another cursor.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Cursor = require("./cursor");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The abstract class about cursors which manipulate another cursor.
+ */
+module.exports = class DecorativeCursor extends Cursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Cursor} cursor - The cursor to be decorated.
+ */
+ constructor(cursor) {
+ super();
+ this.cursor = cursor;
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ const retv = this.cursor.moveNext();
+
+ this.current = this.cursor.current;
+
+ return retv;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/filter-cursor.js b/tools/node_modules/eslint/lib/token-store/filter-cursor.js
new file mode 100644
index 0000000000..7133627bd3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/filter-cursor.js
@@ -0,0 +1,43 @@
+/**
+ * @fileoverview Define the cursor which ignores specified tokens.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const DecorativeCursor = require("./decorative-cursor");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The decorative cursor which ignores specified tokens.
+ */
+module.exports = class FilterCursor extends DecorativeCursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Cursor} cursor - The cursor to be decorated.
+ * @param {Function} predicate - The predicate function to decide tokens this cursor iterates.
+ */
+ constructor(cursor, predicate) {
+ super(cursor);
+ this.predicate = predicate;
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ const predicate = this.predicate;
+
+ while (super.moveNext()) {
+ if (predicate(this.current)) {
+ return true;
+ }
+ }
+ return false;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/forward-token-comment-cursor.js b/tools/node_modules/eslint/lib/token-store/forward-token-comment-cursor.js
new file mode 100644
index 0000000000..be08552970
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/forward-token-comment-cursor.js
@@ -0,0 +1,57 @@
+/**
+ * @fileoverview Define the cursor which iterates tokens and comments.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Cursor = require("./cursor");
+const utils = require("./utils");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The cursor which iterates tokens and comments.
+ */
+module.exports = class ForwardTokenCommentCursor extends Cursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ */
+ constructor(tokens, comments, indexMap, startLoc, endLoc) {
+ super();
+ this.tokens = tokens;
+ this.comments = comments;
+ this.tokenIndex = utils.getFirstIndex(tokens, indexMap, startLoc);
+ this.commentIndex = utils.search(comments, startLoc);
+ this.border = endLoc;
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ const token = (this.tokenIndex < this.tokens.length) ? this.tokens[this.tokenIndex] : null;
+ const comment = (this.commentIndex < this.comments.length) ? this.comments[this.commentIndex] : null;
+
+ if (token && (!comment || token.range[0] < comment.range[0])) {
+ this.current = token;
+ this.tokenIndex += 1;
+ } else if (comment) {
+ this.current = comment;
+ this.commentIndex += 1;
+ } else {
+ this.current = null;
+ }
+
+ return Boolean(this.current) && (this.border === -1 || this.current.range[1] <= this.border);
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/forward-token-cursor.js b/tools/node_modules/eslint/lib/token-store/forward-token-cursor.js
new file mode 100644
index 0000000000..523ed398fa
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/forward-token-cursor.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Define the cursor which iterates tokens only.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Cursor = require("./cursor");
+const utils = require("./utils");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The cursor which iterates tokens only.
+ */
+module.exports = class ForwardTokenCursor extends Cursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ */
+ constructor(tokens, comments, indexMap, startLoc, endLoc) {
+ super();
+ this.tokens = tokens;
+ this.index = utils.getFirstIndex(tokens, indexMap, startLoc);
+ this.indexEnd = utils.getLastIndex(tokens, indexMap, endLoc);
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ if (this.index <= this.indexEnd) {
+ this.current = this.tokens[this.index];
+ this.index += 1;
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ *
+ * Shorthand for performance.
+ *
+ */
+
+ /** @inheritdoc */
+ getOneToken() {
+ return (this.index <= this.indexEnd) ? this.tokens[this.index] : null;
+ }
+
+ /** @inheritdoc */
+ getAllTokens() {
+ return this.tokens.slice(this.index, this.indexEnd + 1);
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/index.js b/tools/node_modules/eslint/lib/token-store/index.js
new file mode 100644
index 0000000000..1446b9ff02
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/index.js
@@ -0,0 +1,633 @@
+/**
+ * @fileoverview Object to handle access and retrieval of tokens.
+ * @author Brandon Mills
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("assert");
+const cursors = require("./cursors");
+const ForwardTokenCursor = require("./forward-token-cursor");
+const PaddedTokenCursor = require("./padded-token-cursor");
+const utils = require("./utils");
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const TOKENS = Symbol("tokens");
+const COMMENTS = Symbol("comments");
+const INDEX_MAP = Symbol("indexMap");
+
+/**
+ * Creates the map from locations to indices in `tokens`.
+ *
+ * The first/last location of tokens is mapped to the index of the token.
+ * The first/last location of comments is mapped to the index of the next token of each comment.
+ *
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @returns {Object} The map from locations to indices in `tokens`.
+ * @private
+ */
+function createIndexMap(tokens, comments) {
+ const map = Object.create(null);
+ let tokenIndex = 0;
+ let commentIndex = 0;
+ let nextStart = 0;
+ let range = null;
+
+ while (tokenIndex < tokens.length || commentIndex < comments.length) {
+ nextStart = (commentIndex < comments.length) ? comments[commentIndex].range[0] : Number.MAX_SAFE_INTEGER;
+ while (tokenIndex < tokens.length && (range = tokens[tokenIndex].range)[0] < nextStart) {
+ map[range[0]] = tokenIndex;
+ map[range[1] - 1] = tokenIndex;
+ tokenIndex += 1;
+ }
+
+ nextStart = (tokenIndex < tokens.length) ? tokens[tokenIndex].range[0] : Number.MAX_SAFE_INTEGER;
+ while (commentIndex < comments.length && (range = comments[commentIndex].range)[0] < nextStart) {
+ map[range[0]] = tokenIndex;
+ map[range[1] - 1] = tokenIndex;
+ commentIndex += 1;
+ }
+ }
+
+ return map;
+}
+
+/**
+ * Creates the cursor iterates tokens with options.
+ *
+ * @param {CursorFactory} factory - The cursor factory to initialize cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {number|Function|Object} [opts=0] - The option object. If this is a number then it's `opts.skip`. If this is a function then it's `opts.filter`.
+ * @param {boolean} [opts.includeComments=false] - The flag to iterate comments as well.
+ * @param {Function|null} [opts.filter=null] - The predicate function to choose tokens.
+ * @param {number} [opts.skip=0] - The count of tokens the cursor skips.
+ * @returns {Cursor} The created cursor.
+ * @private
+ */
+function createCursorWithSkip(factory, tokens, comments, indexMap, startLoc, endLoc, opts) {
+ let includeComments = false;
+ let skip = 0;
+ let filter = null;
+
+ if (typeof opts === "number") {
+ skip = opts | 0;
+ } else if (typeof opts === "function") {
+ filter = opts;
+ } else if (opts) {
+ includeComments = !!opts.includeComments;
+ skip = opts.skip | 0;
+ filter = opts.filter || null;
+ }
+ assert(skip >= 0, "options.skip should be zero or a positive integer.");
+ assert(!filter || typeof filter === "function", "options.filter should be a function.");
+
+ return factory.createCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments, filter, skip, -1);
+}
+
+/**
+ * Creates the cursor iterates tokens with options.
+ *
+ * @param {CursorFactory} factory - The cursor factory to initialize cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {number|Function|Object} [opts=0] - The option object. If this is a number then it's `opts.count`. If this is a function then it's `opts.filter`.
+ * @param {boolean} [opts.includeComments] - The flag to iterate comments as well.
+ * @param {Function|null} [opts.filter=null] - The predicate function to choose tokens.
+ * @param {number} [opts.count=0] - The maximum count of tokens the cursor iterates. Zero is no iteration for backward compatibility.
+ * @returns {Cursor} The created cursor.
+ * @private
+ */
+function createCursorWithCount(factory, tokens, comments, indexMap, startLoc, endLoc, opts) {
+ let includeComments = false;
+ let count = 0;
+ let countExists = false;
+ let filter = null;
+
+ if (typeof opts === "number") {
+ count = opts | 0;
+ countExists = true;
+ } else if (typeof opts === "function") {
+ filter = opts;
+ } else if (opts) {
+ includeComments = !!opts.includeComments;
+ count = opts.count | 0;
+ countExists = typeof opts.count === "number";
+ filter = opts.filter || null;
+ }
+ assert(count >= 0, "options.count should be zero or a positive integer.");
+ assert(!filter || typeof filter === "function", "options.filter should be a function.");
+
+ return factory.createCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments, filter, 0, countExists ? count : -1);
+}
+
+/**
+ * Creates the cursor iterates tokens with options.
+ * This is overload function of the below.
+ *
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {Function|Object} opts - The option object. If this is a function then it's `opts.filter`.
+ * @param {boolean} [opts.includeComments] - The flag to iterate comments as well.
+ * @param {Function|null} [opts.filter=null] - The predicate function to choose tokens.
+ * @param {number} [opts.count=0] - The maximum count of tokens the cursor iterates. Zero is no iteration for backward compatibility.
+ * @returns {Cursor} The created cursor.
+ * @private
+ */
+/**
+ * Creates the cursor iterates tokens with options.
+ *
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {number} [beforeCount=0] - The number of tokens before the node to retrieve.
+ * @param {boolean} [afterCount=0] - The number of tokens after the node to retrieve.
+ * @returns {Cursor} The created cursor.
+ * @private
+ */
+function createCursorWithPadding(tokens, comments, indexMap, startLoc, endLoc, beforeCount, afterCount) {
+ if (typeof beforeCount === "undefined" && typeof afterCount === "undefined") {
+ return new ForwardTokenCursor(tokens, comments, indexMap, startLoc, endLoc);
+ }
+ if (typeof beforeCount === "number" || typeof beforeCount === "undefined") {
+ return new PaddedTokenCursor(tokens, comments, indexMap, startLoc, endLoc, beforeCount | 0, afterCount | 0);
+ }
+ return createCursorWithCount(cursors.forward, tokens, comments, indexMap, startLoc, endLoc, beforeCount);
+}
+
+/**
+ * Gets comment tokens that are adjacent to the current cursor position.
+ * @param {Cursor} cursor - A cursor instance.
+ * @returns {Array} An array of comment tokens adjacent to the current cursor position.
+ * @private
+ */
+function getAdjacentCommentTokensFromCursor(cursor) {
+ const tokens = [];
+ let currentToken = cursor.getOneToken();
+
+ while (currentToken && astUtils.isCommentToken(currentToken)) {
+ tokens.push(currentToken);
+ currentToken = cursor.getOneToken();
+ }
+
+ return tokens;
+}
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The token store.
+ *
+ * This class provides methods to get tokens by locations as fast as possible.
+ * The methods are a part of public API, so we should be careful if it changes this class.
+ *
+ * People can get tokens in O(1) by the hash map which is mapping from the location of tokens/comments to tokens.
+ * Also people can get a mix of tokens and comments in O(log k), the k is the number of comments.
+ * Assuming that comments to be much fewer than tokens, this does not make hash map from token's locations to comments to reduce memory cost.
+ * This uses binary-searching instead for comments.
+ */
+module.exports = class TokenStore {
+
+ /**
+ * Initializes this token store.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ */
+ constructor(tokens, comments) {
+ this[TOKENS] = tokens;
+ this[COMMENTS] = comments;
+ this[INDEX_MAP] = createIndexMap(tokens, comments);
+ }
+
+ //--------------------------------------------------------------------------
+ // Gets single token.
+ //--------------------------------------------------------------------------
+
+ /**
+ * Gets the token starting at the specified index.
+ * @param {number} offset - Index of the start of the token's range.
+ * @param {Object} [options=0] - The option object.
+ * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
+ * @returns {Token|null} The token starting at index, or null if no such token.
+ */
+ getTokenByRangeStart(offset, options) {
+ const includeComments = options && options.includeComments;
+ const token = cursors.forward.createBaseCursor(
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ offset,
+ -1,
+ includeComments
+ ).getOneToken();
+
+ if (token && token.range[0] === offset) {
+ return token;
+ }
+ return null;
+ }
+
+ /**
+ * Gets the first token of the given node.
+ * @param {ASTNode} node - The AST node.
+ * @param {number|Function|Object} [options=0] - The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`.
+ * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
+ * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
+ * @param {number} [options.skip=0] - The count of tokens the cursor skips.
+ * @returns {Token|null} An object representing the token.
+ */
+ getFirstToken(node, options) {
+ return createCursorWithSkip(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[0],
+ node.range[1],
+ options
+ ).getOneToken();
+ }
+
+ /**
+ * Gets the last token of the given node.
+ * @param {ASTNode} node - The AST node.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
+ * @returns {Token|null} An object representing the token.
+ */
+ getLastToken(node, options) {
+ return createCursorWithSkip(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[0],
+ node.range[1],
+ options
+ ).getOneToken();
+ }
+
+ /**
+ * Gets the token that precedes a given node or token.
+ * @param {ASTNode|Token|Comment} node - The AST node or token.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
+ * @returns {Token|null} An object representing the token.
+ */
+ getTokenBefore(node, options) {
+ return createCursorWithSkip(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ -1,
+ node.range[0],
+ options
+ ).getOneToken();
+ }
+
+ /**
+ * Gets the token that follows a given node or token.
+ * @param {ASTNode|Token|Comment} node - The AST node or token.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
+ * @returns {Token|null} An object representing the token.
+ */
+ getTokenAfter(node, options) {
+ return createCursorWithSkip(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[1],
+ -1,
+ options
+ ).getOneToken();
+ }
+
+ /**
+ * Gets the first token between two non-overlapping nodes.
+ * @param {ASTNode|Token|Comment} left - Node before the desired token range.
+ * @param {ASTNode|Token|Comment} right - Node after the desired token range.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
+ * @returns {Token|null} An object representing the token.
+ */
+ getFirstTokenBetween(left, right, options) {
+ return createCursorWithSkip(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ left.range[1],
+ right.range[0],
+ options
+ ).getOneToken();
+ }
+
+ /**
+ * Gets the last token between two non-overlapping nodes.
+ * @param {ASTNode|Token|Comment} left Node before the desired token range.
+ * @param {ASTNode|Token|Comment} right Node after the desired token range.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
+ * @returns {Token|null} An object representing the token.
+ */
+ getLastTokenBetween(left, right, options) {
+ return createCursorWithSkip(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ left.range[1],
+ right.range[0],
+ options
+ ).getOneToken();
+ }
+
+ /**
+ * Gets the token that precedes a given node or token in the token stream.
+ * This is defined for backward compatibility. Use `includeComments` option instead.
+ * TODO: We have a plan to remove this in a future major version.
+ * @param {ASTNode|Token|Comment} node The AST node or token.
+ * @param {number} [skip=0] A number of tokens to skip.
+ * @returns {Token|null} An object representing the token.
+ * @deprecated
+ */
+ getTokenOrCommentBefore(node, skip) {
+ return this.getTokenBefore(node, { includeComments: true, skip });
+ }
+
+ /**
+ * Gets the token that follows a given node or token in the token stream.
+ * This is defined for backward compatibility. Use `includeComments` option instead.
+ * TODO: We have a plan to remove this in a future major version.
+ * @param {ASTNode|Token|Comment} node The AST node or token.
+ * @param {number} [skip=0] A number of tokens to skip.
+ * @returns {Token|null} An object representing the token.
+ * @deprecated
+ */
+ getTokenOrCommentAfter(node, skip) {
+ return this.getTokenAfter(node, { includeComments: true, skip });
+ }
+
+ //--------------------------------------------------------------------------
+ // Gets multiple tokens.
+ //--------------------------------------------------------------------------
+
+ /**
+ * Gets the first `count` tokens of the given node.
+ * @param {ASTNode} node - The AST node.
+ * @param {number|Function|Object} [options=0] - The option object. If this is a number then it's `options.count`. If this is a function then it's `options.filter`.
+ * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
+ * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
+ * @param {number} [options.count=0] - The maximum count of tokens the cursor iterates.
+ * @returns {Token[]} Tokens.
+ */
+ getFirstTokens(node, options) {
+ return createCursorWithCount(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[0],
+ node.range[1],
+ options
+ ).getAllTokens();
+ }
+
+ /**
+ * Gets the last `count` tokens of the given node.
+ * @param {ASTNode} node - The AST node.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
+ * @returns {Token[]} Tokens.
+ */
+ getLastTokens(node, options) {
+ return createCursorWithCount(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[0],
+ node.range[1],
+ options
+ ).getAllTokens().reverse();
+ }
+
+ /**
+ * Gets the `count` tokens that precedes a given node or token.
+ * @param {ASTNode|Token|Comment} node - The AST node or token.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
+ * @returns {Token[]} Tokens.
+ */
+ getTokensBefore(node, options) {
+ return createCursorWithCount(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ -1,
+ node.range[0],
+ options
+ ).getAllTokens().reverse();
+ }
+
+ /**
+ * Gets the `count` tokens that follows a given node or token.
+ * @param {ASTNode|Token|Comment} node - The AST node or token.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
+ * @returns {Token[]} Tokens.
+ */
+ getTokensAfter(node, options) {
+ return createCursorWithCount(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[1],
+ -1,
+ options
+ ).getAllTokens();
+ }
+
+ /**
+ * Gets the first `count` tokens between two non-overlapping nodes.
+ * @param {ASTNode|Token|Comment} left - Node before the desired token range.
+ * @param {ASTNode|Token|Comment} right - Node after the desired token range.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
+ * @returns {Token[]} Tokens between left and right.
+ */
+ getFirstTokensBetween(left, right, options) {
+ return createCursorWithCount(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ left.range[1],
+ right.range[0],
+ options
+ ).getAllTokens();
+ }
+
+ /**
+ * Gets the last `count` tokens between two non-overlapping nodes.
+ * @param {ASTNode|Token|Comment} left Node before the desired token range.
+ * @param {ASTNode|Token|Comment} right Node after the desired token range.
+ * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
+ * @returns {Token[]} Tokens between left and right.
+ */
+ getLastTokensBetween(left, right, options) {
+ return createCursorWithCount(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ left.range[1],
+ right.range[0],
+ options
+ ).getAllTokens().reverse();
+ }
+
+ /**
+ * Gets all tokens that are related to the given node.
+ * @param {ASTNode} node - The AST node.
+ * @param {Function|Object} options The option object. If this is a function then it's `options.filter`.
+ * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
+ * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
+ * @param {number} [options.count=0] - The maximum count of tokens the cursor iterates.
+ * @returns {Token[]} Array of objects representing tokens.
+ */
+ /**
+ * Gets all tokens that are related to the given node.
+ * @param {ASTNode} node - The AST node.
+ * @param {int} [beforeCount=0] - The number of tokens before the node to retrieve.
+ * @param {int} [afterCount=0] - The number of tokens after the node to retrieve.
+ * @returns {Token[]} Array of objects representing tokens.
+ */
+ getTokens(node, beforeCount, afterCount) {
+ return createCursorWithPadding(
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ node.range[0],
+ node.range[1],
+ beforeCount,
+ afterCount
+ ).getAllTokens();
+ }
+
+ /**
+ * Gets all of the tokens between two non-overlapping nodes.
+ * @param {ASTNode|Token|Comment} left Node before the desired token range.
+ * @param {ASTNode|Token|Comment} right Node after the desired token range.
+ * @param {Function|Object} options The option object. If this is a function then it's `options.filter`.
+ * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
+ * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
+ * @param {number} [options.count=0] - The maximum count of tokens the cursor iterates.
+ * @returns {Token[]} Tokens between left and right.
+ */
+ /**
+ * Gets all of the tokens between two non-overlapping nodes.
+ * @param {ASTNode|Token|Comment} left Node before the desired token range.
+ * @param {ASTNode|Token|Comment} right Node after the desired token range.
+ * @param {int} [padding=0] Number of extra tokens on either side of center.
+ * @returns {Token[]} Tokens between left and right.
+ */
+ getTokensBetween(left, right, padding) {
+ return createCursorWithPadding(
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ left.range[1],
+ right.range[0],
+ padding,
+ padding
+ ).getAllTokens();
+ }
+
+ //--------------------------------------------------------------------------
+ // Others.
+ //--------------------------------------------------------------------------
+
+ /**
+ * Checks whether any comments exist or not between the given 2 nodes.
+ *
+ * @param {ASTNode} left - The node to check.
+ * @param {ASTNode} right - The node to check.
+ * @returns {boolean} `true` if one or more comments exist.
+ */
+ commentsExistBetween(left, right) {
+ const index = utils.search(this[COMMENTS], left.range[1]);
+
+ return (
+ index < this[COMMENTS].length &&
+ this[COMMENTS][index].range[1] <= right.range[0]
+ );
+ }
+
+ /**
+ * Gets all comment tokens directly before the given node or token.
+ * @param {ASTNode|token} nodeOrToken The AST node or token to check for adjacent comment tokens.
+ * @returns {Array} An array of comments in occurrence order.
+ */
+ getCommentsBefore(nodeOrToken) {
+ const cursor = createCursorWithCount(
+ cursors.backward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ -1,
+ nodeOrToken.range[0],
+ { includeComments: true }
+ );
+
+ return getAdjacentCommentTokensFromCursor(cursor).reverse();
+ }
+
+ /**
+ * Gets all comment tokens directly after the given node or token.
+ * @param {ASTNode|token} nodeOrToken The AST node or token to check for adjacent comment tokens.
+ * @returns {Array} An array of comments in occurrence order.
+ */
+ getCommentsAfter(nodeOrToken) {
+ const cursor = createCursorWithCount(
+ cursors.forward,
+ this[TOKENS],
+ this[COMMENTS],
+ this[INDEX_MAP],
+ nodeOrToken.range[1],
+ -1,
+ { includeComments: true }
+ );
+
+ return getAdjacentCommentTokensFromCursor(cursor);
+ }
+
+ /**
+ * Gets all comment tokens inside the given node.
+ * @param {ASTNode} node The AST node to get the comments for.
+ * @returns {Array} An array of comments in occurrence order.
+ */
+ getCommentsInside(node) {
+ return this.getTokens(node, {
+ includeComments: true,
+ filter: astUtils.isCommentToken
+ });
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/limit-cursor.js b/tools/node_modules/eslint/lib/token-store/limit-cursor.js
new file mode 100644
index 0000000000..efb46cf0e3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/limit-cursor.js
@@ -0,0 +1,40 @@
+/**
+ * @fileoverview Define the cursor which limits the number of tokens.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const DecorativeCursor = require("./decorative-cursor");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The decorative cursor which limits the number of tokens.
+ */
+module.exports = class LimitCursor extends DecorativeCursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Cursor} cursor - The cursor to be decorated.
+ * @param {number} count - The count of tokens this cursor iterates.
+ */
+ constructor(cursor, count) {
+ super(cursor);
+ this.count = count;
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ if (this.count > 0) {
+ this.count -= 1;
+ return super.moveNext();
+ }
+ return false;
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/padded-token-cursor.js b/tools/node_modules/eslint/lib/token-store/padded-token-cursor.js
new file mode 100644
index 0000000000..c083aed1e9
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/padded-token-cursor.js
@@ -0,0 +1,38 @@
+/**
+ * @fileoverview Define the cursor which iterates tokens only, with inflated range.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const ForwardTokenCursor = require("./forward-token-cursor");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The cursor which iterates tokens only, with inflated range.
+ * This is for the backward compatibility of padding options.
+ */
+module.exports = class PaddedTokenCursor extends ForwardTokenCursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Token[]} tokens - The array of tokens.
+ * @param {Comment[]} comments - The array of comments.
+ * @param {Object} indexMap - The map from locations to indices in `tokens`.
+ * @param {number} startLoc - The start location of the iteration range.
+ * @param {number} endLoc - The end location of the iteration range.
+ * @param {number} beforeCount - The number of tokens this cursor iterates before start.
+ * @param {number} afterCount - The number of tokens this cursor iterates after end.
+ */
+ constructor(tokens, comments, indexMap, startLoc, endLoc, beforeCount, afterCount) {
+ super(tokens, comments, indexMap, startLoc, endLoc);
+ this.index = Math.max(0, this.index - beforeCount);
+ this.indexEnd = Math.min(tokens.length - 1, this.indexEnd + afterCount);
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/skip-cursor.js b/tools/node_modules/eslint/lib/token-store/skip-cursor.js
new file mode 100644
index 0000000000..ab34dfab0d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/skip-cursor.js
@@ -0,0 +1,42 @@
+/**
+ * @fileoverview Define the cursor which ignores the first few tokens.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const DecorativeCursor = require("./decorative-cursor");
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * The decorative cursor which ignores the first few tokens.
+ */
+module.exports = class SkipCursor extends DecorativeCursor {
+
+ /**
+ * Initializes this cursor.
+ * @param {Cursor} cursor - The cursor to be decorated.
+ * @param {number} count - The count of tokens this cursor skips.
+ */
+ constructor(cursor, count) {
+ super(cursor);
+ this.count = count;
+ }
+
+ /** @inheritdoc */
+ moveNext() {
+ while (this.count > 0) {
+ this.count -= 1;
+ if (!super.moveNext()) {
+ return false;
+ }
+ }
+ return super.moveNext();
+ }
+};
diff --git a/tools/node_modules/eslint/lib/token-store/utils.js b/tools/node_modules/eslint/lib/token-store/utils.js
new file mode 100644
index 0000000000..34b0a9af6d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/token-store/utils.js
@@ -0,0 +1,104 @@
+/**
+ * @fileoverview Define utilify functions for token store.
+ * @author Toru Nagashima
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const lodash = require("lodash");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets `token.range[0]` from the given token.
+ *
+ * @param {Node|Token|Comment} token - The token to get.
+ * @returns {number} The start location.
+ * @private
+ */
+function getStartLocation(token) {
+ return token.range[0];
+}
+
+//------------------------------------------------------------------------------
+// Exports
+//------------------------------------------------------------------------------
+
+/**
+ * Binary-searches the index of the first token which is after the given location.
+ * If it was not found, this returns `tokens.length`.
+ *
+ * @param {(Token|Comment)[]} tokens - It searches the token in this list.
+ * @param {number} location - The location to search.
+ * @returns {number} The found index or `tokens.length`.
+ */
+exports.search = function search(tokens, location) {
+ return lodash.sortedIndexBy(
+ tokens,
+ { range: [location] },
+ getStartLocation
+ );
+};
+
+/**
+ * Gets the index of the `startLoc` in `tokens`.
+ * `startLoc` can be the value of `node.range[1]`, so this checks about `startLoc - 1` as well.
+ *
+ * @param {(Token|Comment)[]} tokens - The tokens to find an index.
+ * @param {Object} indexMap - The map from locations to indices.
+ * @param {number} startLoc - The location to get an index.
+ * @returns {number} The index.
+ */
+exports.getFirstIndex = function getFirstIndex(tokens, indexMap, startLoc) {
+ if (startLoc in indexMap) {
+ return indexMap[startLoc];
+ }
+ if ((startLoc - 1) in indexMap) {
+ const index = indexMap[startLoc - 1];
+ const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
+
+ /*
+ * For the map of "comment's location -> token's index", it points the next token of a comment.
+ * In that case, +1 is unnecessary.
+ */
+ if (token && token.range[0] >= startLoc) {
+ return index;
+ }
+ return index + 1;
+ }
+ return 0;
+};
+
+/**
+ * Gets the index of the `endLoc` in `tokens`.
+ * The information of end locations are recorded at `endLoc - 1` in `indexMap`, so this checks about `endLoc - 1` as well.
+ *
+ * @param {(Token|Comment)[]} tokens - The tokens to find an index.
+ * @param {Object} indexMap - The map from locations to indices.
+ * @param {number} endLoc - The location to get an index.
+ * @returns {number} The index.
+ */
+exports.getLastIndex = function getLastIndex(tokens, indexMap, endLoc) {
+ if (endLoc in indexMap) {
+ return indexMap[endLoc] - 1;
+ }
+ if ((endLoc - 1) in indexMap) {
+ const index = indexMap[endLoc - 1];
+ const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
+
+ /*
+ * For the map of "comment's location -> token's index", it points the next token of a comment.
+ * In that case, -1 is necessary.
+ */
+ if (token && token.range[1] > endLoc) {
+ return index - 1;
+ }
+ return index;
+ }
+ return tokens.length - 1;
+};
diff --git a/tools/node_modules/eslint/lib/util/ajv.js b/tools/node_modules/eslint/lib/util/ajv.js
new file mode 100644
index 0000000000..f9e8b98535
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/ajv.js
@@ -0,0 +1,29 @@
+/**
+ * @fileoverview The instance of Ajv validator.
+ * @author Evgeny Poberezkin
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Ajv = require("ajv"),
+ metaSchema = require("ajv/lib/refs/json-schema-draft-04.json");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+const ajv = new Ajv({
+ meta: false,
+ validateSchema: false,
+ missingRefs: "ignore",
+ verbose: true
+});
+
+ajv.addMetaSchema(metaSchema);
+// eslint-disable-next-line no-underscore-dangle
+ajv._opts.defaultMeta = metaSchema.id;
+
+module.exports = ajv;
diff --git a/tools/node_modules/eslint/lib/util/apply-disable-directives.js b/tools/node_modules/eslint/lib/util/apply-disable-directives.js
new file mode 100644
index 0000000000..8153942789
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/apply-disable-directives.js
@@ -0,0 +1,160 @@
+/**
+ * @fileoverview A module that filters reported problems based on `eslint-disable` and `eslint-enable` comments
+ * @author Teddy Katz
+ */
+
+"use strict";
+
+const lodash = require("lodash");
+
+/**
+ * Compares the locations of two objects in a source file
+ * @param {{line: number, column: number}} itemA The first object
+ * @param {{line: number, column: number}} itemB The second object
+ * @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if
+ * itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location.
+ */
+function compareLocations(itemA, itemB) {
+ return itemA.line - itemB.line || itemA.column - itemB.column;
+}
+
+/**
+ * This is the same as the exported function, except that it
+ * doesn't handle disable-line and disable-next-line directives, and it always reports unused
+ * disable directives.
+ * @param {Object} options options for applying directives. This is the same as the options
+ * for the exported function, except that `reportUnusedDisableDirectives` is not supported
+ * (this function always reports unused disable directives).
+ * @returns {{problems: Problem[], unusedDisableDirectives: Problem[]}} An object with a list
+ * of filtered problems and unused eslint-disable directives
+ */
+function applyDirectives(options) {
+ const problems = [];
+ let nextDirectiveIndex = 0;
+ let currentGlobalDisableDirective = null;
+ const disabledRuleMap = new Map();
+
+ // enabledRules is only used when there is a current global disable directive.
+ const enabledRules = new Set();
+ const usedDisableDirectives = new Set();
+
+ for (const problem of options.problems) {
+ while (
+ nextDirectiveIndex < options.directives.length &&
+ compareLocations(options.directives[nextDirectiveIndex], problem) <= 0
+ ) {
+ const directive = options.directives[nextDirectiveIndex++];
+
+ switch (directive.type) {
+ case "disable":
+ if (directive.ruleId === null) {
+ currentGlobalDisableDirective = directive;
+ disabledRuleMap.clear();
+ enabledRules.clear();
+ } else if (currentGlobalDisableDirective) {
+ enabledRules.delete(directive.ruleId);
+ disabledRuleMap.set(directive.ruleId, directive);
+ } else {
+ disabledRuleMap.set(directive.ruleId, directive);
+ }
+ break;
+
+ case "enable":
+ if (directive.ruleId === null) {
+ currentGlobalDisableDirective = null;
+ disabledRuleMap.clear();
+ } else if (currentGlobalDisableDirective) {
+ enabledRules.add(directive.ruleId);
+ disabledRuleMap.delete(directive.ruleId);
+ } else {
+ disabledRuleMap.delete(directive.ruleId);
+ }
+ break;
+
+ // no default
+ }
+ }
+
+ if (disabledRuleMap.has(problem.ruleId)) {
+ usedDisableDirectives.add(disabledRuleMap.get(problem.ruleId));
+ } else if (currentGlobalDisableDirective && !enabledRules.has(problem.ruleId)) {
+ usedDisableDirectives.add(currentGlobalDisableDirective);
+ } else {
+ problems.push(problem);
+ }
+ }
+
+ const unusedDisableDirectives = options.directives
+ .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive))
+ .map(directive => ({
+ ruleId: null,
+ message: directive.ruleId
+ ? `Unused eslint-disable directive (no problems were reported from '${directive.ruleId}').`
+ : "Unused eslint-disable directive (no problems were reported).",
+ line: directive.unprocessedDirective.line,
+ column: directive.unprocessedDirective.column,
+ severity: 2,
+ source: null,
+ nodeType: null
+ }));
+
+ return { problems, unusedDisableDirectives };
+}
+
+/**
+ * Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
+ * of reported problems, determines which problems should be reported.
+ * @param {Object} options Information about directives and problems
+ * @param {{
+ * type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
+ * ruleId: (string|null),
+ * line: number,
+ * column: number
+ * }} options.directives Directive comments found in the file, with one-based columns.
+ * Two directive comments can only have the same location if they also have the same type (e.g. a single eslint-disable
+ * comment for two different rules is represented as two directives).
+ * @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
+ * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
+ * @param {boolean} options.reportUnusedDisableDirectives If `true`, adds additional problems for unused directives
+ * @returns {{ruleId: (string|null), line: number, column: number}[]}
+ * A list of reported problems that were not disabled by the directive comments.
+ */
+module.exports = options => {
+ const blockDirectives = options.directives
+ .filter(directive => directive.type === "disable" || directive.type === "enable")
+ .map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
+ .sort(compareLocations);
+
+ const lineDirectives = lodash.flatMap(options.directives, directive => {
+ switch (directive.type) {
+ case "disable":
+ case "enable":
+ return [];
+
+ case "disable-line":
+ return [
+ { type: "disable", line: directive.line, column: 1, ruleId: directive.ruleId, unprocessedDirective: directive },
+ { type: "enable", line: directive.line + 1, column: 0, ruleId: directive.ruleId, unprocessedDirective: directive }
+ ];
+
+ case "disable-next-line":
+ return [
+ { type: "disable", line: directive.line + 1, column: 1, ruleId: directive.ruleId, unprocessedDirective: directive },
+ { type: "enable", line: directive.line + 2, column: 0, ruleId: directive.ruleId, unprocessedDirective: directive }
+ ];
+
+ default:
+ throw new TypeError(`Unrecognized directive type '${directive.type}'`);
+ }
+ }).sort(compareLocations);
+
+ const blockDirectivesResult = applyDirectives({ problems: options.problems, directives: blockDirectives });
+ const lineDirectivesResult = applyDirectives({ problems: blockDirectivesResult.problems, directives: lineDirectives });
+
+ return options.reportUnusedDisableDirectives
+ ? lineDirectivesResult.problems
+ .concat(blockDirectivesResult.unusedDisableDirectives)
+ .concat(lineDirectivesResult.unusedDisableDirectives)
+ .sort(compareLocations)
+ : lineDirectivesResult.problems;
+};
diff --git a/tools/node_modules/eslint/lib/util/fix-tracker.js b/tools/node_modules/eslint/lib/util/fix-tracker.js
new file mode 100644
index 0000000000..067070df00
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/fix-tracker.js
@@ -0,0 +1,120 @@
+/**
+ * @fileoverview Helper class to aid in constructing fix commands.
+ * @author Alan Pierce
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * A helper class to combine fix options into a fix command. Currently, it
+ * exposes some "retain" methods that extend the range of the text being
+ * replaced so that other fixes won't touch that region in the same pass.
+ */
+class FixTracker {
+
+ /**
+ * Create a new FixTracker.
+ *
+ * @param {ruleFixer} fixer A ruleFixer instance.
+ * @param {SourceCode} sourceCode A SourceCode object for the current code.
+ */
+ constructor(fixer, sourceCode) {
+ this.fixer = fixer;
+ this.sourceCode = sourceCode;
+ this.retainedRange = null;
+ }
+
+ /**
+ * Mark the given range as "retained", meaning that other fixes may not
+ * may not modify this region in the same pass.
+ *
+ * @param {int[]} range The range to retain.
+ * @returns {FixTracker} The same RuleFixer, for chained calls.
+ */
+ retainRange(range) {
+ this.retainedRange = range;
+ return this;
+ }
+
+ /**
+ * Given a node, find the function containing it (or the entire program) and
+ * mark it as retained, meaning that other fixes may not modify it in this
+ * pass. This is useful for avoiding conflicts in fixes that modify control
+ * flow.
+ *
+ * @param {ASTNode} node The node to use as a starting point.
+ * @returns {FixTracker} The same RuleFixer, for chained calls.
+ */
+ retainEnclosingFunction(node) {
+ const functionNode = astUtils.getUpperFunction(node);
+
+ return this.retainRange(functionNode ? functionNode.range : this.sourceCode.ast.range);
+ }
+
+ /**
+ * Given a node or token, find the token before and afterward, and mark that
+ * range as retained, meaning that other fixes may not modify it in this
+ * pass. This is useful for avoiding conflicts in fixes that make a small
+ * change to the code where the AST should not be changed.
+ *
+ * @param {ASTNode|Token} nodeOrToken The node or token to use as a starting
+ * point. The token to the left and right are use in the range.
+ * @returns {FixTracker} The same RuleFixer, for chained calls.
+ */
+ retainSurroundingTokens(nodeOrToken) {
+ const tokenBefore = this.sourceCode.getTokenBefore(nodeOrToken) || nodeOrToken;
+ const tokenAfter = this.sourceCode.getTokenAfter(nodeOrToken) || nodeOrToken;
+
+ return this.retainRange([tokenBefore.range[0], tokenAfter.range[1]]);
+ }
+
+ /**
+ * Create a fix command that replaces the given range with the given text,
+ * accounting for any retained ranges.
+ *
+ * @param {int[]} range The range to remove in the fix.
+ * @param {string} text The text to insert in place of the range.
+ * @returns {Object} The fix command.
+ */
+ replaceTextRange(range, text) {
+ let actualRange;
+
+ if (this.retainedRange) {
+ actualRange = [
+ Math.min(this.retainedRange[0], range[0]),
+ Math.max(this.retainedRange[1], range[1])
+ ];
+ } else {
+ actualRange = range;
+ }
+
+ return this.fixer.replaceTextRange(
+ actualRange,
+ this.sourceCode.text.slice(actualRange[0], range[0]) +
+ text +
+ this.sourceCode.text.slice(range[1], actualRange[1])
+ );
+ }
+
+ /**
+ * Create a fix command that removes the given node or token, accounting for
+ * any retained ranges.
+ *
+ * @param {ASTNode|Token} nodeOrToken The node or token to remove.
+ * @returns {Object} The fix command.
+ */
+ remove(nodeOrToken) {
+ return this.replaceTextRange(nodeOrToken.range, "");
+ }
+}
+
+module.exports = FixTracker;
diff --git a/tools/node_modules/eslint/lib/util/glob-util.js b/tools/node_modules/eslint/lib/util/glob-util.js
new file mode 100644
index 0000000000..6a1f150a59
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/glob-util.js
@@ -0,0 +1,182 @@
+/**
+ * @fileoverview Utilities for working with globs and the filesystem.
+ * @author Ian VanSchooten
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ path = require("path"),
+ GlobSync = require("./glob"),
+
+ pathUtil = require("./path-util"),
+ IgnoredPaths = require("../ignored-paths");
+
+const debug = require("debug")("eslint:glob-util");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks if a provided path is a directory and returns a glob string matching
+ * all files under that directory if so, the path itself otherwise.
+ *
+ * Reason for this is that `glob` needs `/**` to collect all the files under a
+ * directory where as our previous implementation without `glob` simply walked
+ * a directory that is passed. So this is to maintain backwards compatibility.
+ *
+ * Also makes sure all path separators are POSIX style for `glob` compatibility.
+ *
+ * @param {Object} [options] An options object
+ * @param {string[]} [options.extensions=[".js"]] An array of accepted extensions
+ * @param {string} [options.cwd=process.cwd()] The cwd to use to resolve relative pathnames
+ * @returns {Function} A function that takes a pathname and returns a glob that
+ * matches all files with the provided extensions if
+ * pathname is a directory.
+ */
+function processPath(options) {
+ const cwd = (options && options.cwd) || process.cwd();
+ let extensions = (options && options.extensions) || [".js"];
+
+ extensions = extensions.map(ext => ext.replace(/^\./, ""));
+
+ let suffix = "/**";
+
+ if (extensions.length === 1) {
+ suffix += `/*.${extensions[0]}`;
+ } else {
+ suffix += `/*.{${extensions.join(",")}}`;
+ }
+
+ /**
+ * A function that converts a directory name to a glob pattern
+ *
+ * @param {string} pathname The directory path to be modified
+ * @returns {string} The glob path or the file path itself
+ * @private
+ */
+ return function(pathname) {
+ let newPath = pathname;
+ const resolvedPath = path.resolve(cwd, pathname);
+
+ if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) {
+ newPath = pathname.replace(/[/\\]$/, "") + suffix;
+ }
+
+ return pathUtil.convertPathToPosix(newPath);
+ };
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Resolves any directory patterns into glob-based patterns for easier handling.
+ * @param {string[]} patterns File patterns (such as passed on the command line).
+ * @param {Object} options An options object.
+ * @returns {string[]} The equivalent glob patterns and filepath strings.
+ */
+function resolveFileGlobPatterns(patterns, options) {
+
+ const processPathExtensions = processPath(options);
+
+ return patterns.filter(p => p.length).map(processPathExtensions);
+}
+
+/**
+ * Build a list of absolute filesnames on which ESLint will act.
+ * Ignored files are excluded from the results, as are duplicates.
+ *
+ * @param {string[]} globPatterns Glob patterns.
+ * @param {Object} [options] An options object.
+ * @param {string} [options.cwd] CWD (considered for relative filenames)
+ * @param {boolean} [options.ignore] False disables use of .eslintignore.
+ * @param {string} [options.ignorePath] The ignore file to use instead of .eslintignore.
+ * @param {string} [options.ignorePattern] A pattern of files to ignore.
+ * @returns {string[]} Resolved absolute filenames.
+ */
+function listFilesToProcess(globPatterns, options) {
+ options = options || { ignore: true };
+ const files = [],
+ added = {};
+
+ const cwd = (options && options.cwd) || process.cwd();
+
+ /**
+ * Executes the linter on a file defined by the `filename`. Skips
+ * unsupported file extensions and any files that are already linted.
+ * @param {string} filename The file to be processed
+ * @param {boolean} shouldWarnIgnored Whether or not a report should be made if
+ * the file is ignored
+ * @param {IgnoredPaths} ignoredPaths An instance of IgnoredPaths
+ * @returns {void}
+ */
+ function addFile(filename, shouldWarnIgnored, ignoredPaths) {
+ let ignored = false;
+ let isSilentlyIgnored;
+
+ if (ignoredPaths.contains(filename, "default")) {
+ ignored = (options.ignore !== false) && shouldWarnIgnored;
+ isSilentlyIgnored = !shouldWarnIgnored;
+ }
+
+ if (options.ignore !== false) {
+ if (ignoredPaths.contains(filename, "custom")) {
+ if (shouldWarnIgnored) {
+ ignored = true;
+ } else {
+ isSilentlyIgnored = true;
+ }
+ }
+ }
+
+ if (isSilentlyIgnored && !ignored) {
+ return;
+ }
+
+ if (added[filename]) {
+ return;
+ }
+ files.push({ filename, ignored });
+ added[filename] = true;
+ }
+
+ debug("Creating list of files to process.");
+ globPatterns.forEach(pattern => {
+ const file = path.resolve(cwd, pattern);
+
+ if (fs.existsSync(file) && fs.statSync(file).isFile()) {
+ const ignoredPaths = new IgnoredPaths(options);
+
+ addFile(fs.realpathSync(file), true, ignoredPaths);
+ } else {
+
+ // regex to find .hidden or /.hidden patterns, but not ./relative or ../relative
+ const globIncludesDotfiles = /(?:(?:^\.)|(?:[/\\]\.))[^/\\.].*/.test(pattern);
+
+ const ignoredPaths = new IgnoredPaths(Object.assign({}, options, { dotfiles: options.dotfiles || globIncludesDotfiles }));
+ const shouldIgnore = ignoredPaths.getIgnoredFoldersGlobChecker();
+ const globOptions = {
+ nodir: true,
+ dot: true,
+ cwd
+ };
+
+ new GlobSync(pattern, globOptions, shouldIgnore).found.forEach(globMatch => {
+ addFile(path.resolve(cwd, globMatch), false, ignoredPaths);
+ });
+ }
+ });
+
+ return files;
+}
+
+module.exports = {
+ resolveFileGlobPatterns,
+ listFilesToProcess
+};
diff --git a/tools/node_modules/eslint/lib/util/glob.js b/tools/node_modules/eslint/lib/util/glob.js
new file mode 100644
index 0000000000..f352dae7a4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/glob.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview An inherited `glob.GlobSync` to support .gitignore patterns.
+ * @author Kael Zhang
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Sync = require("glob").GlobSync,
+ util = require("util");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const IGNORE = Symbol("ignore");
+
+/**
+ * Subclass of `glob.GlobSync`
+ * @param {string} pattern Pattern to be matched.
+ * @param {Object} options `options` for `glob`
+ * @param {function()} shouldIgnore Method to check whether a directory should be ignored.
+ * @constructor
+ */
+function GlobSync(pattern, options, shouldIgnore) {
+
+ /**
+ * We don't put this thing to argument `options` to avoid
+ * further problems, such as `options` validation.
+ *
+ * Use `Symbol` as much as possible to avoid confliction.
+ */
+ this[IGNORE] = shouldIgnore;
+
+ Sync.call(this, pattern, options);
+}
+
+util.inherits(GlobSync, Sync);
+
+/* eslint no-underscore-dangle: ["error", { "allow": ["_readdir", "_mark"] }] */
+
+GlobSync.prototype._readdir = function(abs, inGlobStar) {
+
+ /**
+ * `options.nodir` makes `options.mark` as `true`.
+ * Mark `abs` first
+ * to make sure `"node_modules"` will be ignored immediately with ignore pattern `"node_modules/"`.
+ *
+ * There is a built-in cache about marked `File.Stat` in `glob`, so that we could not worry about the extra invocation of `this._mark()`
+ */
+ const marked = this._mark(abs);
+
+ if (this[IGNORE](marked)) {
+ return null;
+ }
+
+ return Sync.prototype._readdir.call(this, abs, inGlobStar);
+};
+
+
+module.exports = GlobSync;
diff --git a/tools/node_modules/eslint/lib/util/hash.js b/tools/node_modules/eslint/lib/util/hash.js
new file mode 100644
index 0000000000..6d7ef8bf1b
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/hash.js
@@ -0,0 +1,35 @@
+/**
+ * @fileoverview Defining the hashing function in one place.
+ * @author Michael Ficarra
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const murmur = require("imurmurhash");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * hash the given string
+ * @param {string} str the string to hash
+ * @returns {string} the hash
+ */
+function hash(str) {
+ return murmur(str).result().toString(36);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = hash;
diff --git a/tools/node_modules/eslint/lib/util/keywords.js b/tools/node_modules/eslint/lib/util/keywords.js
new file mode 100644
index 0000000000..3fbb77771d
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/keywords.js
@@ -0,0 +1,67 @@
+/**
+ * @fileoverview A shared list of ES3 keywords.
+ * @author Josh Perez
+ */
+"use strict";
+
+module.exports = [
+ "abstract",
+ "boolean",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "const",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "export",
+ "extends",
+ "false",
+ "final",
+ "finally",
+ "float",
+ "for",
+ "function",
+ "goto",
+ "if",
+ "implements",
+ "import",
+ "in",
+ "instanceof",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "new",
+ "null",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "return",
+ "short",
+ "static",
+ "super",
+ "switch",
+ "synchronized",
+ "this",
+ "throw",
+ "throws",
+ "transient",
+ "true",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "volatile",
+ "while",
+ "with"
+];
diff --git a/tools/node_modules/eslint/lib/util/module-resolver.js b/tools/node_modules/eslint/lib/util/module-resolver.js
new file mode 100644
index 0000000000..470a54f7c4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/module-resolver.js
@@ -0,0 +1,85 @@
+/**
+ * @fileoverview Implements the Node.js require.resolve algorithm
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const Module = require("module");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const DEFAULT_OPTIONS = {
+
+ /*
+ * module.paths is an array of paths to search for resolving things relative
+ * to this file. Module.globalPaths contains all of the special Node.js
+ * directories that can also be searched for modules.
+ *
+ * Need to check for existence of module.paths because Jest seems not to
+ * include it. See https://github.com/eslint/eslint/issues/5791.
+ */
+ lookupPaths: module.paths ? module.paths.concat(Module.globalPaths) : Module.globalPaths.concat()
+};
+
+/**
+ * Resolves modules based on a set of options.
+ */
+class ModuleResolver {
+
+ /**
+ * Resolves modules based on a set of options.
+ * @param {Object} options The options for resolving modules.
+ * @param {string[]} options.lookupPaths An array of paths to include in the
+ * lookup with the highest priority paths coming first.
+ */
+ constructor(options) {
+ this.options = Object.assign({}, DEFAULT_OPTIONS, options || {});
+ }
+
+ /**
+ * Resolves the file location of a given module relative to the configured
+ * lookup paths.
+ * @param {string} name The module name to resolve.
+ * @param {string} extraLookupPath An extra path to look into for the module.
+ * This path is used with the highest priority.
+ * @returns {string} The resolved file path for the module.
+ * @throws {Error} If the module cannot be resolved.
+ */
+ resolve(name, extraLookupPath) {
+
+ /*
+ * First, clone the lookup paths so we're not messing things up for
+ * subsequent calls to this function. Then, move the extraLookupPath to the
+ * top of the lookup paths list so it will be searched first.
+ */
+ const lookupPaths = this.options.lookupPaths.concat();
+
+ lookupPaths.unshift(extraLookupPath);
+
+ /**
+ * Module._findPath is an internal method to Node.js, then one they use to
+ * lookup file paths when require() is called. So, we are hooking into the
+ * exact same logic that Node.js uses.
+ */
+ const result = Module._findPath(name, lookupPaths); // eslint-disable-line no-underscore-dangle
+
+ if (!result) {
+ throw new Error(`Cannot find module '${name}'`);
+ }
+
+ return result;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Public API
+//------------------------------------------------------------------------------
+
+module.exports = ModuleResolver;
diff --git a/tools/node_modules/eslint/lib/util/naming.js b/tools/node_modules/eslint/lib/util/naming.js
new file mode 100644
index 0000000000..dcac81bbd6
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/naming.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Common helpers for naming of plugins, formatters and configs
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const pathUtil = require("../util/path-util");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const NAMESPACE_REGEX = /^@.*\//i;
+
+/**
+ * Brings package name to correct format based on prefix
+ * @param {string} name The name of the package.
+ * @param {string} prefix Can be either "eslint-plugin", "eslint-config" or "eslint-formatter"
+ * @returns {string} Normalized name of the package
+ * @private
+ */
+function normalizePackageName(name, prefix) {
+
+ /**
+ * On Windows, name can come in with Windows slashes instead of Unix slashes.
+ * Normalize to Unix first to avoid errors later on.
+ * https://github.com/eslint/eslint/issues/5644
+ */
+ if (name.indexOf("\\") > -1) {
+ name = pathUtil.convertPathToPosix(name);
+ }
+
+ if (name.charAt(0) === "@") {
+
+ /**
+ * it's a scoped package
+ * package name is the prefix, or just a username
+ */
+ const scopedPackageShortcutRegex = new RegExp(`^(@[^/]+)(?:/(?:${prefix})?)?$`),
+ scopedPackageNameRegex = new RegExp(`^${prefix}(-|$)`);
+
+ if (scopedPackageShortcutRegex.test(name)) {
+ name = name.replace(scopedPackageShortcutRegex, `$1/${prefix}`);
+ } else if (!scopedPackageNameRegex.test(name.split("/")[1])) {
+
+ /**
+ * for scoped packages, insert the prefix after the first / unless
+ * the path is already @scope/eslint or @scope/eslint-xxx-yyy
+ */
+ name = name.replace(/^@([^/]+)\/(.*)$/, `@$1/${prefix}-$2`);
+ }
+ } else if (name.indexOf(`${prefix}-`) !== 0) {
+ name = `${prefix}-${name}`;
+ }
+
+ return name;
+}
+
+/**
+ * Removes the prefix from a term.
+ * @param {string} prefix The prefix to remove.
+ * @param {string} term The term which may have the prefix.
+ * @returns {string} The term without prefix.
+ */
+function removePrefixFromTerm(prefix, term) {
+ return term.startsWith(prefix) ? term.slice(prefix.length) : term;
+}
+
+/**
+ * Adds a prefix to a term.
+ * @param {string} prefix The prefix to add.
+ * @param {string} term The term which may not have the prefix.
+ * @returns {string} The term with prefix.
+ */
+function addPrefixToTerm(prefix, term) {
+ return term.startsWith(prefix) ? term : `${prefix}${term}`;
+}
+
+/**
+ * Gets the scope (namespace) of a term.
+ * @param {string} term The term which may have the namespace.
+ * @returns {string} The namepace of the term if it has one.
+ */
+function getNamespaceFromTerm(term) {
+ const match = term.match(NAMESPACE_REGEX);
+
+ return match ? match[0] : "";
+}
+
+/**
+ * Removes the namespace from a term.
+ * @param {string} term The term which may have the namespace.
+ * @returns {string} The name of the plugin without the namespace.
+ */
+function removeNamespaceFromTerm(term) {
+ return term.replace(NAMESPACE_REGEX, "");
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ normalizePackageName,
+ removePrefixFromTerm,
+ addPrefixToTerm,
+ getNamespaceFromTerm,
+ removeNamespaceFromTerm
+};
diff --git a/tools/node_modules/eslint/lib/util/node-event-generator.js b/tools/node_modules/eslint/lib/util/node-event-generator.js
new file mode 100644
index 0000000000..9d477bbb4e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/node-event-generator.js
@@ -0,0 +1,308 @@
+/**
+ * @fileoverview The event generator for AST nodes.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const esquery = require("esquery");
+const lodash = require("lodash");
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * An object describing an AST selector
+ * @typedef {Object} ASTSelector
+ * @property {string} rawSelector The string that was parsed into this selector
+ * @property {boolean} isExit `true` if this should be emitted when exiting the node rather than when entering
+ * @property {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
+ * @property {string[]|null} listenerTypes A list of node types that could possibly cause the selector to match,
+ * or `null` if all node types could cause a match
+ * @property {number} attributeCount The total number of classes, pseudo-classes, and attribute queries in this selector
+ * @property {number} identifierCount The total number of identifier queries in this selector
+ */
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets the possible types of a selector
+ * @param {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
+ * @returns {string[]|null} The node types that could possibly trigger this selector, or `null` if all node types could trigger it
+ */
+function getPossibleTypes(parsedSelector) {
+ switch (parsedSelector.type) {
+ case "identifier":
+ return [parsedSelector.value];
+
+ case "matches": {
+ const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
+
+ if (typesForComponents.every(typesForComponent => typesForComponent)) {
+ return lodash.union.apply(null, typesForComponents);
+ }
+ return null;
+ }
+
+ case "compound": {
+ const typesForComponents = parsedSelector.selectors.map(getPossibleTypes).filter(typesForComponent => typesForComponent);
+
+ // If all of the components could match any type, then the compound could also match any type.
+ if (!typesForComponents.length) {
+ return null;
+ }
+
+ /*
+ * If at least one of the components could only match a particular type, the compound could only match
+ * the intersection of those types.
+ */
+ return lodash.intersection.apply(null, typesForComponents);
+ }
+
+ case "child":
+ case "descendant":
+ case "sibling":
+ case "adjacent":
+ return getPossibleTypes(parsedSelector.right);
+
+ default:
+ return null;
+
+ }
+}
+
+/**
+ * Counts the number of class, pseudo-class, and attribute queries in this selector
+ * @param {Object} parsedSelector An object (from esquery) describing the selector's matching behavior
+ * @returns {number} The number of class, pseudo-class, and attribute queries in this selector
+ */
+function countClassAttributes(parsedSelector) {
+ switch (parsedSelector.type) {
+ case "child":
+ case "descendant":
+ case "sibling":
+ case "adjacent":
+ return countClassAttributes(parsedSelector.left) + countClassAttributes(parsedSelector.right);
+
+ case "compound":
+ case "not":
+ case "matches":
+ return parsedSelector.selectors.reduce((sum, childSelector) => sum + countClassAttributes(childSelector), 0);
+
+ case "attribute":
+ case "field":
+ case "nth-child":
+ case "nth-last-child":
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Counts the number of identifier queries in this selector
+ * @param {Object} parsedSelector An object (from esquery) describing the selector's matching behavior
+ * @returns {number} The number of identifier queries
+ */
+function countIdentifiers(parsedSelector) {
+ switch (parsedSelector.type) {
+ case "child":
+ case "descendant":
+ case "sibling":
+ case "adjacent":
+ return countIdentifiers(parsedSelector.left) + countIdentifiers(parsedSelector.right);
+
+ case "compound":
+ case "not":
+ case "matches":
+ return parsedSelector.selectors.reduce((sum, childSelector) => sum + countIdentifiers(childSelector), 0);
+
+ case "identifier":
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Compares the specificity of two selector objects, with CSS-like rules.
+ * @param {ASTSelector} selectorA An AST selector descriptor
+ * @param {ASTSelector} selectorB Another AST selector descriptor
+ * @returns {number}
+ * a value less than 0 if selectorA is less specific than selectorB
+ * a value greater than 0 if selectorA is more specific than selectorB
+ * a value less than 0 if selectorA and selectorB have the same specificity, and selectorA <= selectorB alphabetically
+ * a value greater than 0 if selectorA and selectorB have the same specificity, and selectorA > selectorB alphabetically
+ */
+function compareSpecificity(selectorA, selectorB) {
+ return selectorA.attributeCount - selectorB.attributeCount ||
+ selectorA.identifierCount - selectorB.identifierCount ||
+ (selectorA.rawSelector <= selectorB.rawSelector ? -1 : 1);
+}
+
+/**
+ * Parses a raw selector string, and throws a useful error if parsing fails.
+ * @param {string} rawSelector A raw AST selector
+ * @returns {Object} An object (from esquery) describing the matching behavior of this selector
+ * @throws {Error} An error if the selector is invalid
+ */
+function tryParseSelector(rawSelector) {
+ try {
+ return esquery.parse(rawSelector.replace(/:exit$/, ""));
+ } catch (err) {
+ if (typeof err.offset === "number") {
+ throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.offset}: ${err.message}`);
+ }
+ throw err;
+ }
+}
+
+/**
+ * Parses a raw selector string, and returns the parsed selector along with specificity and type information.
+ * @param {string} rawSelector A raw AST selector
+ * @returns {ASTSelector} A selector descriptor
+ */
+const parseSelector = lodash.memoize(rawSelector => {
+ const parsedSelector = tryParseSelector(rawSelector);
+
+ return {
+ rawSelector,
+ isExit: rawSelector.endsWith(":exit"),
+ parsedSelector,
+ listenerTypes: getPossibleTypes(parsedSelector),
+ attributeCount: countClassAttributes(parsedSelector),
+ identifierCount: countIdentifiers(parsedSelector)
+ };
+});
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * The event generator for AST nodes.
+ * This implements below interface.
+ *
+ * ```ts
+ * interface EventGenerator {
+ * emitter: SafeEmitter;
+ * enterNode(node: ASTNode): void;
+ * leaveNode(node: ASTNode): void;
+ * }
+ * ```
+ */
+class NodeEventGenerator {
+
+ /**
+ * @param {SafeEmitter} emitter
+ * An SafeEmitter which is the destination of events. This emitter must already
+ * have registered listeners for all of the events that it needs to listen for.
+ * (See lib/util/safe-emitter.js for more details on `SafeEmitter`.)
+ * @returns {NodeEventGenerator} new instance
+ */
+ constructor(emitter) {
+ this.emitter = emitter;
+ this.currentAncestry = [];
+ this.enterSelectorsByNodeType = new Map();
+ this.exitSelectorsByNodeType = new Map();
+ this.anyTypeEnterSelectors = [];
+ this.anyTypeExitSelectors = [];
+
+ emitter.eventNames().forEach(rawSelector => {
+ const selector = parseSelector(rawSelector);
+
+ if (selector.listenerTypes) {
+ selector.listenerTypes.forEach(nodeType => {
+ const typeMap = selector.isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType;
+
+ if (!typeMap.has(nodeType)) {
+ typeMap.set(nodeType, []);
+ }
+ typeMap.get(nodeType).push(selector);
+ });
+ } else {
+ (selector.isExit ? this.anyTypeExitSelectors : this.anyTypeEnterSelectors).push(selector);
+ }
+ });
+
+ this.anyTypeEnterSelectors.sort(compareSpecificity);
+ this.anyTypeExitSelectors.sort(compareSpecificity);
+ this.enterSelectorsByNodeType.forEach(selectorList => selectorList.sort(compareSpecificity));
+ this.exitSelectorsByNodeType.forEach(selectorList => selectorList.sort(compareSpecificity));
+ }
+
+ /**
+ * Checks a selector against a node, and emits it if it matches
+ * @param {ASTNode} node The node to check
+ * @param {ASTSelector} selector An AST selector descriptor
+ * @returns {void}
+ */
+ applySelector(node, selector) {
+ if (esquery.matches(node, selector.parsedSelector, this.currentAncestry)) {
+ this.emitter.emit(selector.rawSelector, node);
+ }
+ }
+
+ /**
+ * Applies all appropriate selectors to a node, in specificity order
+ * @param {ASTNode} node The node to check
+ * @param {boolean} isExit `false` if the node is currently being entered, `true` if it's currently being exited
+ * @returns {void}
+ */
+ applySelectors(node, isExit) {
+ const selectorsByNodeType = (isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType).get(node.type) || [];
+ const anyTypeSelectors = isExit ? this.anyTypeExitSelectors : this.anyTypeEnterSelectors;
+
+ /*
+ * selectorsByNodeType and anyTypeSelectors were already sorted by specificity in the constructor.
+ * Iterate through each of them, applying selectors in the right order.
+ */
+ let selectorsByTypeIndex = 0;
+ let anyTypeSelectorsIndex = 0;
+
+ while (selectorsByTypeIndex < selectorsByNodeType.length || anyTypeSelectorsIndex < anyTypeSelectors.length) {
+ if (
+ selectorsByTypeIndex >= selectorsByNodeType.length ||
+ anyTypeSelectorsIndex < anyTypeSelectors.length &&
+ compareSpecificity(anyTypeSelectors[anyTypeSelectorsIndex], selectorsByNodeType[selectorsByTypeIndex]) < 0
+ ) {
+ this.applySelector(node, anyTypeSelectors[anyTypeSelectorsIndex++]);
+ } else {
+ this.applySelector(node, selectorsByNodeType[selectorsByTypeIndex++]);
+ }
+ }
+ }
+
+ /**
+ * Emits an event of entering AST node.
+ * @param {ASTNode} node - A node which was entered.
+ * @returns {void}
+ */
+ enterNode(node) {
+ if (node.parent) {
+ this.currentAncestry.unshift(node.parent);
+ }
+ this.applySelectors(node, false);
+ }
+
+ /**
+ * Emits an event of leaving AST node.
+ * @param {ASTNode} node - A node which was left.
+ * @returns {void}
+ */
+ leaveNode(node) {
+ this.applySelectors(node, true);
+ this.currentAncestry.shift();
+ }
+}
+
+module.exports = NodeEventGenerator;
diff --git a/tools/node_modules/eslint/lib/util/npm-util.js b/tools/node_modules/eslint/lib/util/npm-util.js
new file mode 100644
index 0000000000..6c431e0395
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/npm-util.js
@@ -0,0 +1,179 @@
+/**
+ * @fileoverview Utility for executing npm commands.
+ * @author Ian VanSchooten
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const fs = require("fs"),
+ spawn = require("cross-spawn"),
+ path = require("path"),
+ log = require("../logging");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Find the closest package.json file, starting at process.cwd (by default),
+ * and working up to root.
+ *
+ * @param {string} [startDir=process.cwd()] Starting directory
+ * @returns {string} Absolute path to closest package.json file
+ */
+function findPackageJson(startDir) {
+ let dir = path.resolve(startDir || process.cwd());
+
+ do {
+ const pkgFile = path.join(dir, "package.json");
+
+ if (!fs.existsSync(pkgFile) || !fs.statSync(pkgFile).isFile()) {
+ dir = path.join(dir, "..");
+ continue;
+ }
+ return pkgFile;
+ } while (dir !== path.resolve(dir, ".."));
+ return null;
+}
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Install node modules synchronously and save to devDependencies in package.json
+ * @param {string|string[]} packages Node module or modules to install
+ * @returns {void}
+ */
+function installSyncSaveDev(packages) {
+ if (!Array.isArray(packages)) {
+ packages = [packages];
+ }
+ const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packages),
+ { stdio: "inherit" });
+ const error = npmProcess.error;
+
+ if (error && error.code === "ENOENT") {
+ const pluralS = packages.length > 1 ? "s" : "";
+
+ log.error(`Could not execute npm. Please install the following package${pluralS} with your package manager of choice: ${packages.join(", ")}`);
+ }
+}
+
+/**
+ * Fetch `peerDependencies` of the given package by `npm show` command.
+ * @param {string} packageName The package name to fetch peerDependencies.
+ * @returns {Object} Gotten peerDependencies. Returns null if npm was not found.
+ */
+function fetchPeerDependencies(packageName) {
+ const npmProcess = spawn.sync(
+ "npm",
+ ["show", "--json", packageName, "peerDependencies"],
+ { encoding: "utf8" }
+ );
+
+ const error = npmProcess.error;
+
+ if (error && error.code === "ENOENT") {
+ return null;
+ }
+ const fetchedText = npmProcess.stdout.trim();
+
+ return JSON.parse(fetchedText || "{}");
+
+
+}
+
+/**
+ * Check whether node modules are include in a project's package.json.
+ *
+ * @param {string[]} packages Array of node module names
+ * @param {Object} opt Options Object
+ * @param {boolean} opt.dependencies Set to true to check for direct dependencies
+ * @param {boolean} opt.devDependencies Set to true to check for development dependencies
+ * @param {boolean} opt.startdir Directory to begin searching from
+ * @returns {Object} An object whose keys are the module names
+ * and values are booleans indicating installation.
+ */
+function check(packages, opt) {
+ let deps = [];
+ const pkgJson = (opt) ? findPackageJson(opt.startDir) : findPackageJson();
+ let fileJson;
+
+ if (!pkgJson) {
+ throw new Error("Could not find a package.json file. Run 'npm init' to create one.");
+ }
+
+ try {
+ fileJson = JSON.parse(fs.readFileSync(pkgJson, "utf8"));
+ } catch (e) {
+ log.info("Could not read package.json file. Please check that the file contains valid JSON.");
+ throw new Error(e);
+ }
+
+ if (opt.devDependencies && typeof fileJson.devDependencies === "object") {
+ deps = deps.concat(Object.keys(fileJson.devDependencies));
+ }
+ if (opt.dependencies && typeof fileJson.dependencies === "object") {
+ deps = deps.concat(Object.keys(fileJson.dependencies));
+ }
+ return packages.reduce((status, pkg) => {
+ status[pkg] = deps.indexOf(pkg) !== -1;
+ return status;
+ }, {});
+}
+
+/**
+ * Check whether node modules are included in the dependencies of a project's
+ * package.json.
+ *
+ * Convienience wrapper around check().
+ *
+ * @param {string[]} packages Array of node modules to check.
+ * @param {string} rootDir The directory contianing a package.json
+ * @returns {Object} An object whose keys are the module names
+ * and values are booleans indicating installation.
+ */
+function checkDeps(packages, rootDir) {
+ return check(packages, { dependencies: true, startDir: rootDir });
+}
+
+/**
+ * Check whether node modules are included in the devDependencies of a project's
+ * package.json.
+ *
+ * Convienience wrapper around check().
+ *
+ * @param {string[]} packages Array of node modules to check.
+ * @returns {Object} An object whose keys are the module names
+ * and values are booleans indicating installation.
+ */
+function checkDevDeps(packages) {
+ return check(packages, { devDependencies: true });
+}
+
+/**
+ * Check whether package.json is found in current path.
+ *
+ * @param {string=} startDir Starting directory
+ * @returns {boolean} Whether a package.json is found in current path.
+ */
+function checkPackageJson(startDir) {
+ return !!findPackageJson(startDir);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ installSyncSaveDev,
+ fetchPeerDependencies,
+ checkDeps,
+ checkDevDeps,
+ checkPackageJson
+};
diff --git a/tools/node_modules/eslint/lib/util/path-util.js b/tools/node_modules/eslint/lib/util/path-util.js
new file mode 100644
index 0000000000..4100ff91a0
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/path-util.js
@@ -0,0 +1,74 @@
+/**
+ * @fileoverview Common helpers for operations on filenames and paths
+ * @author Ian VanSchooten
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const path = require("path");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Replace Windows with posix style paths
+ *
+ * @param {string} filepath Path to convert
+ * @returns {string} Converted filepath
+ */
+function convertPathToPosix(filepath) {
+ const normalizedFilepath = path.normalize(filepath);
+ const posixFilepath = normalizedFilepath.replace(/\\/g, "/");
+
+ return posixFilepath;
+}
+
+/**
+ * Converts an absolute filepath to a relative path from a given base path
+ *
+ * For example, if the filepath is `/my/awesome/project/foo.bar`,
+ * and the base directory is `/my/awesome/project/`,
+ * then this function should return `foo.bar`.
+ *
+ * path.relative() does something similar, but it requires a baseDir (`from` argument).
+ * This function makes it optional and just removes a leading slash if the baseDir is not given.
+ *
+ * It does not take into account symlinks (for now).
+ *
+ * @param {string} filepath Path to convert to relative path. If already relative,
+ * it will be assumed to be relative to process.cwd(),
+ * converted to absolute, and then processed.
+ * @param {string} [baseDir] Absolute base directory to resolve the filepath from.
+ * If not provided, all this function will do is remove
+ * a leading slash.
+ * @returns {string} Relative filepath
+ */
+function getRelativePath(filepath, baseDir) {
+ let relativePath;
+
+ if (!path.isAbsolute(filepath)) {
+ filepath = path.resolve(filepath);
+ }
+ if (baseDir) {
+ if (!path.isAbsolute(baseDir)) {
+ throw new Error("baseDir should be an absolute path");
+ }
+ relativePath = path.relative(baseDir, filepath);
+ } else {
+ relativePath = filepath.replace(/^\//, "");
+ }
+ return relativePath;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ convertPathToPosix,
+ getRelativePath
+};
diff --git a/tools/node_modules/eslint/lib/util/patterns/letters.js b/tools/node_modules/eslint/lib/util/patterns/letters.js
new file mode 100644
index 0000000000..eb255d8f00
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/patterns/letters.js
@@ -0,0 +1,36 @@
+/**
+ * @fileoverview Pattern for detecting any letter (even letters outside of ASCII).
+ * NOTE: This file was generated using this script in JSCS based on the Unicode 7.0.0 standard: https://github.com/jscs-dev/node-jscs/blob/f5ed14427deb7e7aac84f3056a5aab2d9f3e563e/publish/helpers/generate-patterns.js
+ * Do not edit this file by hand-- please use https://github.com/mathiasbynens/regenerate to regenerate the regular expression exported from this file.
+ * @author Kevin Partington
+ * @license MIT License (from JSCS). See below.
+ */
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright 2013-2016 Dulin Marat and other contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+"use strict";
+
+module.exports = /[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/;
diff --git a/tools/node_modules/eslint/lib/util/rule-fixer.js b/tools/node_modules/eslint/lib/util/rule-fixer.js
new file mode 100644
index 0000000000..bdd80d13b1
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/rule-fixer.js
@@ -0,0 +1,140 @@
+/**
+ * @fileoverview An object that creates fix commands for rules.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+// none!
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Creates a fix command that inserts text at the specified index in the source text.
+ * @param {int} index The 0-based index at which to insert the new text.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ * @private
+ */
+function insertTextAt(index, text) {
+ return {
+ range: [index, index],
+ text
+ };
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Creates code fixing commands for rules.
+ */
+
+const ruleFixer = Object.freeze({
+
+ /**
+ * Creates a fix command that inserts text after the given node or token.
+ * The fix is not applied until applyFixes() is called.
+ * @param {ASTNode|Token} nodeOrToken The node or token to insert after.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ */
+ insertTextAfter(nodeOrToken, text) {
+ return this.insertTextAfterRange(nodeOrToken.range, text);
+ },
+
+ /**
+ * Creates a fix command that inserts text after the specified range in the source text.
+ * The fix is not applied until applyFixes() is called.
+ * @param {int[]} range The range to replace, first item is start of range, second
+ * is end of range.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ */
+ insertTextAfterRange(range, text) {
+ return insertTextAt(range[1], text);
+ },
+
+ /**
+ * Creates a fix command that inserts text before the given node or token.
+ * The fix is not applied until applyFixes() is called.
+ * @param {ASTNode|Token} nodeOrToken The node or token to insert before.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ */
+ insertTextBefore(nodeOrToken, text) {
+ return this.insertTextBeforeRange(nodeOrToken.range, text);
+ },
+
+ /**
+ * Creates a fix command that inserts text before the specified range in the source text.
+ * The fix is not applied until applyFixes() is called.
+ * @param {int[]} range The range to replace, first item is start of range, second
+ * is end of range.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ */
+ insertTextBeforeRange(range, text) {
+ return insertTextAt(range[0], text);
+ },
+
+ /**
+ * Creates a fix command that replaces text at the node or token.
+ * The fix is not applied until applyFixes() is called.
+ * @param {ASTNode|Token} nodeOrToken The node or token to remove.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ */
+ replaceText(nodeOrToken, text) {
+ return this.replaceTextRange(nodeOrToken.range, text);
+ },
+
+ /**
+ * Creates a fix command that replaces text at the specified range in the source text.
+ * The fix is not applied until applyFixes() is called.
+ * @param {int[]} range The range to replace, first item is start of range, second
+ * is end of range.
+ * @param {string} text The text to insert.
+ * @returns {Object} The fix command.
+ */
+ replaceTextRange(range, text) {
+ return {
+ range,
+ text
+ };
+ },
+
+ /**
+ * Creates a fix command that removes the node or token from the source.
+ * The fix is not applied until applyFixes() is called.
+ * @param {ASTNode|Token} nodeOrToken The node or token to remove.
+ * @returns {Object} The fix command.
+ */
+ remove(nodeOrToken) {
+ return this.removeRange(nodeOrToken.range);
+ },
+
+ /**
+ * Creates a fix command that removes the specified range of text from the source.
+ * The fix is not applied until applyFixes() is called.
+ * @param {int[]} range The range to remove, first item is start of range, second
+ * is end of range.
+ * @returns {Object} The fix command.
+ */
+ removeRange(range) {
+ return {
+ range,
+ text: ""
+ };
+ }
+
+});
+
+
+module.exports = ruleFixer;
diff --git a/tools/node_modules/eslint/lib/util/safe-emitter.js b/tools/node_modules/eslint/lib/util/safe-emitter.js
new file mode 100644
index 0000000000..2fa373cb96
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/safe-emitter.js
@@ -0,0 +1,54 @@
+/**
+ * @fileoverview A variant of EventEmitter which does not give listeners information about each other
+ * @author Teddy Katz
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * An event emitter
+ * @typedef {Object} SafeEmitter
+ * @property {function(eventName: string, listenerFunc: Function): void} on Adds a listener for a given event name
+ * @property {function(eventName: string, arg1?: any, arg2?: any, arg3?: any)} emit Emits an event with a given name.
+ * This calls all the listeners that were listening for that name, with `arg1`, `arg2`, and `arg3` as arguments.
+ * @property {function(): string[]} eventNames Gets the list of event names that have registered listeners.
+ */
+
+/**
+ * Creates an object which can listen for and emit events.
+ * This is similar to the EventEmitter API in Node's standard library, but it has a few differences.
+ * The goal is to allow multiple modules to attach arbitrary listeners to the same emitter, without
+ * letting the modules know about each other at all.
+ * 1. It has no special keys like `error` and `newListener`, which would allow modules to detect when
+ * another module throws an error or registers a listener.
+ * 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a
+ * `this` value of the emitter instance, which would give listeners access to other listeners.)
+ * 3. Events can be emitted with at most 3 arguments. (For example: when using `emitter.emit('foo', a, b, c)`,
+ * the arguments `a`, `b`, and `c` will be passed to the listener functions.)
+ * @returns {SafeEmitter} An emitter
+ */
+module.exports = () => {
+ const listeners = Object.create(null);
+
+ return Object.freeze({
+ on(eventName, listener) {
+ if (eventName in listeners) {
+ listeners[eventName].push(listener);
+ } else {
+ listeners[eventName] = [listener];
+ }
+ },
+ emit(eventName, a, b, c) {
+ if (eventName in listeners) {
+ listeners[eventName].forEach(listener => listener(a, b, c));
+ }
+ },
+ eventNames() {
+ return Object.keys(listeners);
+ }
+ });
+};
diff --git a/tools/node_modules/eslint/lib/util/source-code-fixer.js b/tools/node_modules/eslint/lib/util/source-code-fixer.js
new file mode 100644
index 0000000000..b5bfc7457a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/source-code-fixer.js
@@ -0,0 +1,152 @@
+/**
+ * @fileoverview An object that caches and applies source code fixes.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const debug = require("debug")("eslint:text-fixer");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const BOM = "\uFEFF";
+
+/**
+ * Compares items in a messages array by range.
+ * @param {Message} a The first message.
+ * @param {Message} b The second message.
+ * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
+ * @private
+ */
+function compareMessagesByFixRange(a, b) {
+ return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1];
+}
+
+/**
+ * Compares items in a messages array by line and column.
+ * @param {Message} a The first message.
+ * @param {Message} b The second message.
+ * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
+ * @private
+ */
+function compareMessagesByLocation(a, b) {
+ return a.line - b.line || a.column - b.column;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Utility for apply fixes to source code.
+ * @constructor
+ */
+function SourceCodeFixer() {
+ Object.freeze(this);
+}
+
+/**
+ * Applies the fixes specified by the messages to the given text. Tries to be
+ * smart about the fixes and won't apply fixes over the same area in the text.
+ * @param {string} sourceText The text to apply the changes to.
+ * @param {Message[]} messages The array of messages reported by ESLint.
+ * @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
+ * @returns {Object} An object containing the fixed text and any unfixed messages.
+ */
+SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
+ debug("Applying fixes");
+
+ if (shouldFix === false) {
+ debug("shouldFix parameter was false, not attempting fixes");
+ return {
+ fixed: false,
+ messages,
+ output: sourceText
+ };
+ }
+
+ // clone the array
+ const remainingMessages = [],
+ fixes = [],
+ bom = sourceText.startsWith(BOM) ? BOM : "",
+ text = bom ? sourceText.slice(1) : sourceText;
+ let lastPos = Number.NEGATIVE_INFINITY,
+ output = bom;
+
+ /**
+ * Try to use the 'fix' from a problem.
+ * @param {Message} problem The message object to apply fixes from
+ * @returns {boolean} Whether fix was successfully applied
+ */
+ function attemptFix(problem) {
+ const fix = problem.fix;
+ const start = fix.range[0];
+ const end = fix.range[1];
+
+ // Remain it as a problem if it's overlapped or it's a negative range
+ if (lastPos >= start || start > end) {
+ remainingMessages.push(problem);
+ return false;
+ }
+
+ // Remove BOM.
+ if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
+ output = "";
+ }
+
+ // Make output to this fix.
+ output += text.slice(Math.max(0, lastPos), Math.max(0, start));
+ output += fix.text;
+ lastPos = end;
+ return true;
+ }
+
+ messages.forEach(problem => {
+ if (problem.hasOwnProperty("fix")) {
+ fixes.push(problem);
+ } else {
+ remainingMessages.push(problem);
+ }
+ });
+
+ if (fixes.length) {
+ debug("Found fixes to apply");
+ let fixesWereApplied = false;
+
+ for (const problem of fixes.sort(compareMessagesByFixRange)) {
+ if (typeof shouldFix !== "function" || shouldFix(problem)) {
+ attemptFix(problem);
+
+ /*
+ * The only time attemptFix will fail is if a previous fix was
+ * applied which conflicts with it. So we can mark this as true.
+ */
+ fixesWereApplied = true;
+ } else {
+ remainingMessages.push(problem);
+ }
+ }
+ output += text.slice(Math.max(0, lastPos));
+
+ return {
+ fixed: fixesWereApplied,
+ messages: remainingMessages.sort(compareMessagesByLocation),
+ output
+ };
+ }
+
+ debug("No fixes to apply");
+ return {
+ fixed: false,
+ messages,
+ output: bom + text
+ };
+
+};
+
+module.exports = SourceCodeFixer;
diff --git a/tools/node_modules/eslint/lib/util/source-code-util.js b/tools/node_modules/eslint/lib/util/source-code-util.js
new file mode 100644
index 0000000000..6ffd243e2e
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/source-code-util.js
@@ -0,0 +1,109 @@
+/**
+ * @fileoverview Tools for obtaining SourceCode objects.
+ * @author Ian VanSchooten
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const CLIEngine = require("../cli-engine"),
+ globUtil = require("./glob-util"),
+ baseDefaultOptions = require("../../conf/default-cli-options");
+
+const debug = require("debug")("eslint:source-code-util");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Get the SourceCode object for a single file
+ * @param {string} filename The fully resolved filename to get SourceCode from.
+ * @param {Object} options A CLIEngine options object.
+ * @returns {Array} Array of the SourceCode object representing the file
+ * and fatal error message.
+ */
+function getSourceCodeOfFile(filename, options) {
+ debug("getting sourceCode of", filename);
+ const opts = Object.assign({}, options, { rules: {} });
+ const cli = new CLIEngine(opts);
+ const results = cli.executeOnFiles([filename]);
+
+ if (results && results.results[0] && results.results[0].messages[0] && results.results[0].messages[0].fatal) {
+ const msg = results.results[0].messages[0];
+
+ throw new Error(`(${filename}:${msg.line}:${msg.column}) ${msg.message}`);
+ }
+ const sourceCode = cli.linter.getSourceCode();
+
+ return sourceCode;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+
+/**
+ * This callback is used to measure execution status in a progress bar
+ * @callback progressCallback
+ * @param {number} The total number of times the callback will be called.
+ */
+
+/**
+ * Gets the SourceCode of a single file, or set of files.
+ * @param {string[]|string} patterns A filename, directory name, or glob,
+ * or an array of them
+ * @param {Object} [options] A CLIEngine options object. If not provided,
+ * the default cli options will be used.
+ * @param {progressCallback} [cb] Callback for reporting execution status
+ * @returns {Object} The SourceCode of all processed files.
+ */
+function getSourceCodeOfFiles(patterns, options, cb) {
+ const sourceCodes = {};
+ let opts;
+
+ if (typeof patterns === "string") {
+ patterns = [patterns];
+ }
+
+ const defaultOptions = Object.assign({}, baseDefaultOptions, { cwd: process.cwd() });
+
+ if (typeof options === "undefined") {
+ opts = defaultOptions;
+ } else if (typeof options === "function") {
+ cb = options;
+ opts = defaultOptions;
+ } else if (typeof options === "object") {
+ opts = Object.assign({}, defaultOptions, options);
+ }
+ debug("constructed options:", opts);
+ patterns = globUtil.resolveFileGlobPatterns(patterns, opts);
+
+ const filenames = globUtil.listFilesToProcess(patterns, opts)
+ .filter(fileInfo => !fileInfo.ignored)
+ .reduce((files, fileInfo) => files.concat(fileInfo.filename), []);
+
+ if (filenames.length === 0) {
+ debug(`Did not find any files matching pattern(s): ${patterns}`);
+ }
+ filenames.forEach(filename => {
+ const sourceCode = getSourceCodeOfFile(filename, opts);
+
+ if (sourceCode) {
+ debug("got sourceCode of", filename);
+ sourceCodes[filename] = sourceCode;
+ }
+ if (cb) {
+ cb(filenames.length); // eslint-disable-line callback-return
+ }
+ });
+ return sourceCodes;
+}
+
+module.exports = {
+ getSourceCodeOfFiles
+};
diff --git a/tools/node_modules/eslint/lib/util/source-code.js b/tools/node_modules/eslint/lib/util/source-code.js
new file mode 100644
index 0000000000..0bd710bf67
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/source-code.js
@@ -0,0 +1,472 @@
+/**
+ * @fileoverview Abstraction of JavaScript source code.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const TokenStore = require("../token-store"),
+ Traverser = require("./traverser"),
+ astUtils = require("../ast-utils"),
+ lodash = require("lodash");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Validates that the given AST has the required information.
+ * @param {ASTNode} ast The Program node of the AST to check.
+ * @throws {Error} If the AST doesn't contain the correct information.
+ * @returns {void}
+ * @private
+ */
+function validate(ast) {
+ if (!ast.tokens) {
+ throw new Error("AST is missing the tokens array.");
+ }
+
+ if (!ast.comments) {
+ throw new Error("AST is missing the comments array.");
+ }
+
+ if (!ast.loc) {
+ throw new Error("AST is missing location information.");
+ }
+
+ if (!ast.range) {
+ throw new Error("AST is missing range information");
+ }
+}
+
+/**
+ * Check to see if its a ES6 export declaration.
+ * @param {ASTNode} astNode An AST node.
+ * @returns {boolean} whether the given node represents an export declaration.
+ * @private
+ */
+function looksLikeExport(astNode) {
+ return astNode.type === "ExportDefaultDeclaration" || astNode.type === "ExportNamedDeclaration" ||
+ astNode.type === "ExportAllDeclaration" || astNode.type === "ExportSpecifier";
+}
+
+/**
+ * Merges two sorted lists into a larger sorted list in O(n) time.
+ * @param {Token[]} tokens The list of tokens.
+ * @param {Token[]} comments The list of comments.
+ * @returns {Token[]} A sorted list of tokens and comments.
+ * @private
+ */
+function sortedMerge(tokens, comments) {
+ const result = [];
+ let tokenIndex = 0;
+ let commentIndex = 0;
+
+ while (tokenIndex < tokens.length || commentIndex < comments.length) {
+ if (commentIndex >= comments.length || tokenIndex < tokens.length && tokens[tokenIndex].range[0] < comments[commentIndex].range[0]) {
+ result.push(tokens[tokenIndex++]);
+ } else {
+ result.push(comments[commentIndex++]);
+ }
+ }
+
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+class SourceCode extends TokenStore {
+
+ /**
+ * Represents parsed source code.
+ * @param {string} text - The source code text.
+ * @param {ASTNode} ast - The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
+ * @constructor
+ */
+ constructor(text, ast) {
+ validate(ast);
+
+ super(ast.tokens, ast.comments);
+
+ /**
+ * The flag to indicate that the source code has Unicode BOM.
+ * @type boolean
+ */
+ this.hasBOM = (text.charCodeAt(0) === 0xFEFF);
+
+ /**
+ * The original text source code.
+ * BOM was stripped from this text.
+ * @type string
+ */
+ this.text = (this.hasBOM ? text.slice(1) : text);
+
+ /**
+ * The parsed AST for the source code.
+ * @type ASTNode
+ */
+ this.ast = ast;
+
+ // Check the source text for the presence of a shebang since it is parsed as a standard line comment.
+ const shebangMatched = this.text.match(astUtils.SHEBANG_MATCHER);
+ const hasShebang = shebangMatched && ast.comments.length && ast.comments[0].value === shebangMatched[1];
+
+ if (hasShebang) {
+ ast.comments[0].type = "Shebang";
+ }
+
+ this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);
+
+ /**
+ * The source code split into lines according to ECMA-262 specification.
+ * This is done to avoid each rule needing to do so separately.
+ * @type string[]
+ */
+ this.lines = [];
+ this.lineStartIndices = [0];
+
+ const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
+ let match;
+
+ /*
+ * Previously, this was implemented using a regex that
+ * matched a sequence of non-linebreak characters followed by a
+ * linebreak, then adding the lengths of the matches. However,
+ * this caused a catastrophic backtracking issue when the end
+ * of a file contained a large number of non-newline characters.
+ * To avoid this, the current implementation just matches newlines
+ * and uses match.index to get the correct line start indices.
+ */
+ while ((match = lineEndingPattern.exec(this.text))) {
+ this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index));
+ this.lineStartIndices.push(match.index + match[0].length);
+ }
+ this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1]));
+
+ // Cache for comments found using getComments().
+ this._commentCache = new WeakMap();
+
+ // don't allow modification of this object
+ Object.freeze(this);
+ Object.freeze(this.lines);
+ }
+
+ /**
+ * Split the source code into multiple lines based on the line delimiters.
+ * @param {string} text Source code as a string.
+ * @returns {string[]} Array of source code lines.
+ * @public
+ */
+ static splitLines(text) {
+ return text.split(astUtils.createGlobalLinebreakMatcher());
+ }
+
+ /**
+ * Gets the source code for the given node.
+ * @param {ASTNode=} node The AST node to get the text for.
+ * @param {int=} beforeCount The number of characters before the node to retrieve.
+ * @param {int=} afterCount The number of characters after the node to retrieve.
+ * @returns {string} The text representing the AST node.
+ * @public
+ */
+ getText(node, beforeCount, afterCount) {
+ if (node) {
+ return this.text.slice(Math.max(node.range[0] - (beforeCount || 0), 0),
+ node.range[1] + (afterCount || 0));
+ }
+ return this.text;
+ }
+
+ /**
+ * Gets the entire source text split into an array of lines.
+ * @returns {Array} The source text as an array of lines.
+ * @public
+ */
+ getLines() {
+ return this.lines;
+ }
+
+ /**
+ * Retrieves an array containing all comments in the source code.
+ * @returns {ASTNode[]} An array of comment nodes.
+ * @public
+ */
+ getAllComments() {
+ return this.ast.comments;
+ }
+
+ /**
+ * Gets all comments for the given node.
+ * @param {ASTNode} node The AST node to get the comments for.
+ * @returns {Object} An object containing a leading and trailing array
+ * of comments indexed by their position.
+ * @public
+ */
+ getComments(node) {
+ if (this._commentCache.has(node)) {
+ return this._commentCache.get(node);
+ }
+
+ const comments = {
+ leading: [],
+ trailing: []
+ };
+
+ /*
+ * Return all comments as leading comments of the Program node when
+ * there is no executable code.
+ */
+ if (node.type === "Program") {
+ if (node.body.length === 0) {
+ comments.leading = node.comments;
+ }
+ } else {
+
+ /*
+ * Return comments as trailing comments of nodes that only contain
+ * comments (to mimic the comment attachment behavior present in Espree).
+ */
+ if ((node.type === "BlockStatement" || node.type === "ClassBody") && node.body.length === 0 ||
+ node.type === "ObjectExpression" && node.properties.length === 0 ||
+ node.type === "ArrayExpression" && node.elements.length === 0 ||
+ node.type === "SwitchStatement" && node.cases.length === 0
+ ) {
+ comments.trailing = this.getTokens(node, {
+ includeComments: true,
+ filter: astUtils.isCommentToken
+ });
+ }
+
+ /*
+ * Iterate over tokens before and after node and collect comment tokens.
+ * Do not include comments that exist outside of the parent node
+ * to avoid duplication.
+ */
+ let currentToken = this.getTokenBefore(node, { includeComments: true });
+
+ while (currentToken && astUtils.isCommentToken(currentToken)) {
+ if (node.parent && (currentToken.start < node.parent.start)) {
+ break;
+ }
+ comments.leading.push(currentToken);
+ currentToken = this.getTokenBefore(currentToken, { includeComments: true });
+ }
+
+ comments.leading.reverse();
+
+ currentToken = this.getTokenAfter(node, { includeComments: true });
+
+ while (currentToken && astUtils.isCommentToken(currentToken)) {
+ if (node.parent && (currentToken.end > node.parent.end)) {
+ break;
+ }
+ comments.trailing.push(currentToken);
+ currentToken = this.getTokenAfter(currentToken, { includeComments: true });
+ }
+ }
+
+ this._commentCache.set(node, comments);
+ return comments;
+ }
+
+ /**
+ * Retrieves the JSDoc comment for a given node.
+ * @param {ASTNode} node The AST node to get the comment for.
+ * @returns {Token|null} The Block comment token containing the JSDoc comment
+ * for the given node or null if not found.
+ * @public
+ */
+ getJSDocComment(node) {
+
+ /**
+ * Checks for the presence of a JSDoc comment for the given node and returns it.
+ * @param {ASTNode} astNode The AST node to get the comment for.
+ * @returns {Token|null} The Block comment token containing the JSDoc comment
+ * for the given node or null if not found.
+ * @private
+ */
+ const findJSDocComment = astNode => {
+ const tokenBefore = this.getTokenBefore(astNode, { includeComments: true });
+
+ if (
+ tokenBefore &&
+ astUtils.isCommentToken(tokenBefore) &&
+ tokenBefore.type === "Block" &&
+ tokenBefore.value.charAt(0) === "*" &&
+ astNode.loc.start.line - tokenBefore.loc.end.line <= 1
+ ) {
+ return tokenBefore;
+ }
+
+ return null;
+ };
+ let parent = node.parent;
+
+ switch (node.type) {
+ case "ClassDeclaration":
+ case "FunctionDeclaration":
+ return findJSDocComment(looksLikeExport(parent) ? parent : node);
+
+ case "ClassExpression":
+ return findJSDocComment(parent.parent);
+
+ case "ArrowFunctionExpression":
+ case "FunctionExpression":
+ if (parent.type !== "CallExpression" && parent.type !== "NewExpression") {
+ while (
+ !this.getCommentsBefore(parent).length &&
+ !/Function/.test(parent.type) &&
+ parent.type !== "MethodDefinition" &&
+ parent.type !== "Property"
+ ) {
+ parent = parent.parent;
+
+ if (!parent) {
+ break;
+ }
+ }
+
+ if (parent && parent.type !== "FunctionDeclaration" && parent.type !== "Program") {
+ return findJSDocComment(parent);
+ }
+ }
+
+ return findJSDocComment(node);
+
+ // falls through
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets the deepest node containing a range index.
+ * @param {int} index Range index of the desired node.
+ * @returns {ASTNode} The node if found or null if not found.
+ * @public
+ */
+ getNodeByRangeIndex(index) {
+ let result = null,
+ resultParent = null;
+ const traverser = new Traverser();
+
+ traverser.traverse(this.ast, {
+ enter(node, parent) {
+ if (node.range[0] <= index && index < node.range[1]) {
+ result = node;
+ resultParent = parent;
+ } else {
+ this.skip();
+ }
+ },
+ leave(node) {
+ if (node === result) {
+ this.break();
+ }
+ }
+ });
+
+ return result ? Object.assign({ parent: resultParent }, result) : null;
+ }
+
+ /**
+ * Determines if two tokens have at least one whitespace character
+ * between them. This completely disregards comments in making the
+ * determination, so comments count as zero-length substrings.
+ * @param {Token} first The token to check after.
+ * @param {Token} second The token to check before.
+ * @returns {boolean} True if there is only space between tokens, false
+ * if there is anything other than whitespace between tokens.
+ * @public
+ */
+ isSpaceBetweenTokens(first, second) {
+ const text = this.text.slice(first.range[1], second.range[0]);
+
+ return /\s/.test(text.replace(/\/\*.*?\*\//g, ""));
+ }
+
+ /**
+ * Converts a source text index into a (line, column) pair.
+ * @param {number} index The index of a character in a file
+ * @returns {Object} A {line, column} location object with a 0-indexed column
+ * @public
+ */
+ getLocFromIndex(index) {
+ if (typeof index !== "number") {
+ throw new TypeError("Expected `index` to be a number.");
+ }
+
+ if (index < 0 || index > this.text.length) {
+ throw new RangeError(`Index out of range (requested index ${index}, but source text has length ${this.text.length}).`);
+ }
+
+ /*
+ * For an argument of this.text.length, return the location one "spot" past the last character
+ * of the file. If the last character is a linebreak, the location will be column 0 of the next
+ * line; otherwise, the location will be in the next column on the same line.
+ *
+ * See getIndexFromLoc for the motivation for this special case.
+ */
+ if (index === this.text.length) {
+ return { line: this.lines.length, column: this.lines[this.lines.length - 1].length };
+ }
+
+ /*
+ * To figure out which line rangeIndex is on, determine the last index at which rangeIndex could
+ * be inserted into lineIndices to keep the list sorted.
+ */
+ const lineNumber = lodash.sortedLastIndex(this.lineStartIndices, index);
+
+ return { line: lineNumber, column: index - this.lineStartIndices[lineNumber - 1] };
+ }
+
+ /**
+ * Converts a (line, column) pair into a range index.
+ * @param {Object} loc A line/column location
+ * @param {number} loc.line The line number of the location (1-indexed)
+ * @param {number} loc.column The column number of the location (0-indexed)
+ * @returns {number} The range index of the location in the file.
+ * @public
+ */
+ getIndexFromLoc(loc) {
+ if (typeof loc !== "object" || typeof loc.line !== "number" || typeof loc.column !== "number") {
+ throw new TypeError("Expected `loc` to be an object with numeric `line` and `column` properties.");
+ }
+
+ if (loc.line <= 0) {
+ throw new RangeError(`Line number out of range (line ${loc.line} requested). Line numbers should be 1-based.`);
+ }
+
+ if (loc.line > this.lineStartIndices.length) {
+ throw new RangeError(`Line number out of range (line ${loc.line} requested, but only ${this.lineStartIndices.length} lines present).`);
+ }
+
+ const lineStartIndex = this.lineStartIndices[loc.line - 1];
+ const lineEndIndex = loc.line === this.lineStartIndices.length ? this.text.length : this.lineStartIndices[loc.line];
+ const positionIndex = lineStartIndex + loc.column;
+
+ /*
+ * By design, getIndexFromLoc({ line: lineNum, column: 0 }) should return the start index of
+ * the given line, provided that the line number is valid element of this.lines. Since the
+ * last element of this.lines is an empty string for files with trailing newlines, add a
+ * special case where getting the index for the first location after the end of the file
+ * will return the length of the file, rather than throwing an error. This allows rules to
+ * use getIndexFromLoc consistently without worrying about edge cases at the end of a file.
+ */
+ if (
+ loc.line === this.lineStartIndices.length && positionIndex > lineEndIndex ||
+ loc.line < this.lineStartIndices.length && positionIndex >= lineEndIndex
+ ) {
+ throw new RangeError(`Column number out of range (column ${loc.column} requested, but the length of line ${loc.line} is ${lineEndIndex - lineStartIndex}).`);
+ }
+
+ return positionIndex;
+ }
+}
+
+module.exports = SourceCode;
diff --git a/tools/node_modules/eslint/lib/util/traverser.js b/tools/node_modules/eslint/lib/util/traverser.js
new file mode 100644
index 0000000000..fc070186b3
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/traverser.js
@@ -0,0 +1,45 @@
+/**
+ * @fileoverview Wrapper around estraverse
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const estraverse = require("estraverse");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const KEY_BLACKLIST = new Set([
+ "parent",
+ "leadingComments",
+ "trailingComments"
+]);
+
+/**
+ * Wrapper around an estraverse controller that ensures the correct keys
+ * are visited.
+ * @constructor
+ */
+class Traverser extends estraverse.Controller {
+ traverse(node, visitor) {
+ visitor.fallback = Traverser.getKeys;
+ return super.traverse(node, visitor);
+ }
+
+ /**
+ * Calculates the keys to use for traversal.
+ * @param {ASTNode} node The node to read keys from.
+ * @returns {string[]} An array of keys to visit on the node.
+ * @private
+ */
+ static getKeys(node) {
+ return Object.keys(node).filter(key => !KEY_BLACKLIST.has(key));
+ }
+}
+
+module.exports = Traverser;
diff --git a/tools/node_modules/eslint/lib/util/xml-escape.js b/tools/node_modules/eslint/lib/util/xml-escape.js
new file mode 100644
index 0000000000..9f43c99c46
--- /dev/null
+++ b/tools/node_modules/eslint/lib/util/xml-escape.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview XML character escaper
+ * @author George Chung
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the escaped value for a character
+ * @param {string} s string to examine
+ * @returns {string} severity level
+ * @private
+ */
+module.exports = function(s) {
+ return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/g, c => { // eslint-disable-line no-control-regex
+ switch (c) {
+ case "<":
+ return "&lt;";
+ case ">":
+ return "&gt;";
+ case "&":
+ return "&amp;";
+ case "\"":
+ return "&quot;";
+ case "'":
+ return "&apos;";
+ default:
+ return `&#${c.charCodeAt(0)};`;
+ }
+ });
+};