summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/lib/rules')
-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
258 files changed, 41165 insertions, 0 deletions
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))
+ });
+ }
+
+ }
+ };
+
+ }
+};