eslint 4.7.1 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +113 -0
- package/README.md +34 -19
- package/conf/default-cli-options.js +2 -1
- package/conf/eslint-recommended.js +2 -0
- package/lib/ast-utils.js +2 -1
- package/lib/cli-engine.js +26 -5
- package/lib/cli.js +17 -9
- package/lib/code-path-analysis/code-path-segment.js +39 -39
- package/lib/code-path-analysis/code-path-state.js +3 -0
- package/lib/formatters/html-template-message.html +1 -1
- package/lib/formatters/html-template-page.html +3 -1
- package/lib/formatters/html.js +2 -1
- package/lib/ignored-paths.js +1 -1
- package/lib/linter.js +43 -71
- package/lib/logging.js +2 -2
- package/lib/options.js +12 -0
- package/lib/rules/array-bracket-newline.js +19 -5
- package/lib/rules/block-spacing.js +1 -1
- package/lib/rules/callback-return.js +2 -1
- package/lib/rules/capitalized-comments.js +2 -1
- package/lib/rules/comma-style.js +3 -1
- package/lib/rules/dot-notation.js +56 -35
- package/lib/rules/generator-star-spacing.js +3 -3
- package/lib/rules/indent-legacy.js +5 -2
- package/lib/rules/indent.js +25 -19
- package/lib/rules/lines-around-comment.js +33 -4
- package/lib/rules/lines-between-class-members.js +91 -0
- package/lib/rules/max-len.js +2 -3
- package/lib/rules/multiline-comment-style.js +294 -0
- package/lib/rules/new-cap.js +2 -1
- package/lib/rules/newline-before-return.js +4 -2
- package/lib/rules/no-alert.js +7 -15
- package/lib/rules/no-catch-shadow.js +1 -1
- package/lib/rules/no-constant-condition.js +2 -2
- package/lib/rules/no-control-regex.js +2 -1
- package/lib/rules/no-else-return.js +43 -8
- package/lib/rules/no-extra-parens.js +6 -3
- package/lib/rules/no-lonely-if.js +2 -1
- package/lib/rules/no-loop-func.js +2 -3
- package/lib/rules/no-mixed-requires.js +8 -4
- package/lib/rules/no-restricted-imports.js +86 -17
- package/lib/rules/no-restricted-modules.js +84 -15
- package/lib/rules/no-trailing-spaces.js +1 -1
- package/lib/rules/no-unneeded-ternary.js +3 -1
- package/lib/rules/no-unused-labels.js +2 -1
- package/lib/rules/no-useless-computed-key.js +2 -1
- package/lib/rules/no-useless-escape.js +8 -1
- package/lib/rules/no-var.js +11 -0
- package/lib/rules/object-shorthand.js +6 -2
- package/lib/rules/operator-linebreak.js +3 -1
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/require-jsdoc.js +11 -18
- package/lib/rules/sort-imports.js +6 -3
- package/lib/rules/space-unary-ops.js +6 -8
- package/lib/rules/valid-jsdoc.js +39 -33
- package/lib/testers/rule-tester.js +20 -6
- package/lib/util/apply-disable-directives.js +56 -27
- package/lib/util/node-event-generator.js +6 -20
- package/lib/util/safe-emitter.js +54 -0
- package/lib/util/source-code.js +73 -67
- package/messages/no-config-found.txt +1 -1
- package/package.json +3 -4
- package/lib/internal-rules/.eslintrc.yml +0 -3
- package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
- package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
package/lib/linter.js
CHANGED
@@ -9,8 +9,7 @@
|
|
9
9
|
// Requirements
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const
|
13
|
-
eslintScope = require("eslint-scope"),
|
12
|
+
const eslintScope = require("eslint-scope"),
|
14
13
|
levn = require("levn"),
|
15
14
|
lodash = require("lodash"),
|
16
15
|
blankScriptAST = require("../conf/blank-script.json"),
|
@@ -20,6 +19,7 @@ const EventEmitter = require("events").EventEmitter,
|
|
20
19
|
validator = require("./config/config-validator"),
|
21
20
|
Environments = require("./config/environments"),
|
22
21
|
applyDisableDirectives = require("./util/apply-disable-directives"),
|
22
|
+
createEmitter = require("./util/safe-emitter"),
|
23
23
|
NodeEventGenerator = require("./util/node-event-generator"),
|
24
24
|
SourceCode = require("./util/source-code"),
|
25
25
|
Traverser = require("./util/traverser"),
|
@@ -164,13 +164,12 @@ function parseListConfig(string) {
|
|
164
164
|
* Ensures that variables representing built-in properties of the Global Object,
|
165
165
|
* and any globals declared by special block comments, are present in the global
|
166
166
|
* scope.
|
167
|
-
* @param {ASTNode} program The top node of the AST.
|
168
167
|
* @param {Scope} globalScope The global scope.
|
169
168
|
* @param {Object} config The existing configuration data.
|
170
169
|
* @param {Environments} envContext Env context
|
171
170
|
* @returns {void}
|
172
171
|
*/
|
173
|
-
function addDeclaredGlobals(
|
172
|
+
function addDeclaredGlobals(globalScope, config, envContext) {
|
174
173
|
const declaredGlobals = {},
|
175
174
|
exportedGlobals = {},
|
176
175
|
explicitGlobals = {},
|
@@ -292,7 +291,7 @@ function createDisableDirectives(type, loc, value) {
|
|
292
291
|
*/
|
293
292
|
function modifyConfigsFromComments(filename, ast, config, linterContext) {
|
294
293
|
|
295
|
-
|
294
|
+
const commentConfig = {
|
296
295
|
exported: {},
|
297
296
|
astGlobals: {},
|
298
297
|
rules: {},
|
@@ -321,10 +320,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
|
|
321
320
|
Object.assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
|
322
321
|
break;
|
323
322
|
|
324
|
-
case "eslint-env":
|
325
|
-
Object.assign(commentConfig.env, parseListConfig(value));
|
326
|
-
break;
|
327
|
-
|
328
323
|
case "eslint-disable":
|
329
324
|
[].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
|
330
325
|
break;
|
@@ -362,14 +357,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
|
|
362
357
|
}
|
363
358
|
});
|
364
359
|
|
365
|
-
// apply environment configs
|
366
|
-
Object.keys(commentConfig.env).forEach(name => {
|
367
|
-
const env = linterContext.environments.get(name);
|
368
|
-
|
369
|
-
if (env) {
|
370
|
-
commentConfig = ConfigOps.merge(commentConfig, env);
|
371
|
-
}
|
372
|
-
});
|
373
360
|
Object.assign(commentConfig.rules, commentRules);
|
374
361
|
|
375
362
|
return {
|
@@ -660,22 +647,6 @@ function markVariableAsUsed(scopeManager, currentNode, parserOptions, name) {
|
|
660
647
|
return false;
|
661
648
|
}
|
662
649
|
|
663
|
-
/**
|
664
|
-
* Gets all the ancestors of a given node
|
665
|
-
* @param {ASTNode} node The node
|
666
|
-
* @returns {ASTNode[]} All the ancestor nodes in the AST, not including the provided node, starting
|
667
|
-
* from the root node and going inwards to the parent node.
|
668
|
-
*/
|
669
|
-
function getAncestors(node) {
|
670
|
-
if (node.parent) {
|
671
|
-
const parentAncestors = getAncestors(node.parent);
|
672
|
-
|
673
|
-
parentAncestors.push(node.parent);
|
674
|
-
return parentAncestors;
|
675
|
-
}
|
676
|
-
return [];
|
677
|
-
}
|
678
|
-
|
679
650
|
// methods that exist on SourceCode object
|
680
651
|
const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
|
681
652
|
getSource: "getText",
|
@@ -714,6 +685,8 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
|
|
714
685
|
)
|
715
686
|
);
|
716
687
|
|
688
|
+
const lastSourceCodes = new WeakMap();
|
689
|
+
|
717
690
|
//------------------------------------------------------------------------------
|
718
691
|
// Public Interface
|
719
692
|
//------------------------------------------------------------------------------
|
@@ -725,7 +698,7 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
|
|
725
698
|
module.exports = class Linter {
|
726
699
|
|
727
700
|
constructor() {
|
728
|
-
this
|
701
|
+
lastSourceCodes.set(this, null);
|
729
702
|
this.version = pkg.version;
|
730
703
|
|
731
704
|
this.rules = new Rules();
|
@@ -750,20 +723,24 @@ module.exports = class Linter {
|
|
750
723
|
* @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
|
751
724
|
* If this is not set, the filename will default to '<input>' in the rule context. If
|
752
725
|
* an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
|
753
|
-
* @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
|
726
|
+
* @param {boolean} [filenameOrOptions.allowInlineConfig=true] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
|
754
727
|
* Useful if you want to validate JS without comments overriding rules.
|
728
|
+
* @param {boolean} [filenameOrOptions.reportUnusedDisableDirectives=false] Adds reported errors for unused
|
729
|
+
* eslint-disable directives
|
755
730
|
* @returns {Object[]} The results as an array of messages or null if no messages.
|
756
731
|
*/
|
757
732
|
_verifyWithoutProcessors(textOrSourceCode, config, filenameOrOptions) {
|
758
733
|
let text,
|
759
734
|
parserServices,
|
760
735
|
allowInlineConfig,
|
761
|
-
providedFilename
|
736
|
+
providedFilename,
|
737
|
+
reportUnusedDisableDirectives;
|
762
738
|
|
763
739
|
// evaluate arguments
|
764
740
|
if (typeof filenameOrOptions === "object") {
|
765
741
|
providedFilename = filenameOrOptions.filename;
|
766
742
|
allowInlineConfig = filenameOrOptions.allowInlineConfig;
|
743
|
+
reportUnusedDisableDirectives = filenameOrOptions.reportUnusedDisableDirectives;
|
767
744
|
} else {
|
768
745
|
providedFilename = filenameOrOptions;
|
769
746
|
}
|
@@ -771,11 +748,11 @@ module.exports = class Linter {
|
|
771
748
|
const filename = typeof providedFilename === "string" ? providedFilename : "<input>";
|
772
749
|
|
773
750
|
if (typeof textOrSourceCode === "string") {
|
774
|
-
this
|
751
|
+
lastSourceCodes.set(this, null);
|
775
752
|
text = textOrSourceCode;
|
776
753
|
} else {
|
777
|
-
this
|
778
|
-
text =
|
754
|
+
lastSourceCodes.set(this, textOrSourceCode);
|
755
|
+
text = textOrSourceCode.text;
|
779
756
|
}
|
780
757
|
|
781
758
|
// search and apply "eslint-env *".
|
@@ -794,13 +771,13 @@ module.exports = class Linter {
|
|
794
771
|
// process initial config to make it safe to extend
|
795
772
|
config = prepareConfig(config, this.environments);
|
796
773
|
|
797
|
-
if (this
|
774
|
+
if (lastSourceCodes.get(this)) {
|
798
775
|
parserServices = {};
|
799
776
|
} else {
|
800
777
|
|
801
778
|
// there's no input, just exit here
|
802
779
|
if (text.trim().length === 0) {
|
803
|
-
this
|
780
|
+
lastSourceCodes.set(this, new SourceCode(text, blankScriptAST));
|
804
781
|
return [];
|
805
782
|
}
|
806
783
|
|
@@ -816,11 +793,11 @@ module.exports = class Linter {
|
|
816
793
|
}
|
817
794
|
|
818
795
|
parserServices = parseResult.services;
|
819
|
-
this
|
796
|
+
lastSourceCodes.set(this, new SourceCode(text, parseResult.ast));
|
820
797
|
}
|
821
798
|
|
822
799
|
const problems = [];
|
823
|
-
const sourceCode = this
|
800
|
+
const sourceCode = lastSourceCodes.get(this);
|
824
801
|
let disableDirectives;
|
825
802
|
|
826
803
|
// parse global comments and modify config
|
@@ -834,7 +811,8 @@ module.exports = class Linter {
|
|
834
811
|
disableDirectives = [];
|
835
812
|
}
|
836
813
|
|
837
|
-
const emitter =
|
814
|
+
const emitter = createEmitter();
|
815
|
+
const traverser = new Traverser();
|
838
816
|
const ecmaFeatures = config.parserOptions.ecmaFeatures || {};
|
839
817
|
const ecmaVersion = config.parserOptions.ecmaVersion || 5;
|
840
818
|
const scopeManager = eslintScope.analyze(sourceCode.ast, {
|
@@ -846,19 +824,6 @@ module.exports = class Linter {
|
|
846
824
|
fallback: Traverser.getKeys
|
847
825
|
});
|
848
826
|
|
849
|
-
let currentNode = sourceCode.ast;
|
850
|
-
const nodeQueue = [];
|
851
|
-
|
852
|
-
new Traverser().traverse(sourceCode.ast, {
|
853
|
-
enter(node, parent) {
|
854
|
-
node.parent = parent;
|
855
|
-
nodeQueue.push({ isEntering: true, node });
|
856
|
-
},
|
857
|
-
leave(node) {
|
858
|
-
nodeQueue.push({ isEntering: false, node });
|
859
|
-
}
|
860
|
-
});
|
861
|
-
|
862
827
|
/*
|
863
828
|
* Create a frozen object with the ruleContext properties and methods that are shared by all rules.
|
864
829
|
* All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
|
@@ -868,12 +833,12 @@ module.exports = class Linter {
|
|
868
833
|
Object.assign(
|
869
834
|
Object.create(BASE_TRAVERSAL_CONTEXT),
|
870
835
|
{
|
871
|
-
getAncestors: () =>
|
836
|
+
getAncestors: () => traverser.parents(),
|
872
837
|
getDeclaredVariables: scopeManager.getDeclaredVariables.bind(scopeManager),
|
873
838
|
getFilename: () => filename,
|
874
|
-
getScope: () => getScope(scopeManager,
|
839
|
+
getScope: () => getScope(scopeManager, traverser.current(), config.parserOptions.ecmaVersion),
|
875
840
|
getSourceCode: () => sourceCode,
|
876
|
-
markVariableAsUsed: name => markVariableAsUsed(scopeManager,
|
841
|
+
markVariableAsUsed: name => markVariableAsUsed(scopeManager, traverser.current(), config.parserOptions, name),
|
877
842
|
parserOptions: config.parserOptions,
|
878
843
|
parserPath: config.parser,
|
879
844
|
parserServices,
|
@@ -889,7 +854,7 @@ module.exports = class Linter {
|
|
889
854
|
*/
|
890
855
|
_linter: {
|
891
856
|
report() {},
|
892
|
-
on: emitter.on
|
857
|
+
on: emitter.on
|
893
858
|
}
|
894
859
|
}
|
895
860
|
)
|
@@ -974,23 +939,30 @@ module.exports = class Linter {
|
|
974
939
|
});
|
975
940
|
|
976
941
|
// augment global scope with declared global variables
|
977
|
-
addDeclaredGlobals(
|
942
|
+
addDeclaredGlobals(scopeManager.scopes[0], config, this.environments);
|
978
943
|
|
979
944
|
const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter));
|
980
945
|
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
946
|
+
/*
|
947
|
+
* Each node has a type property. Whenever a particular type of
|
948
|
+
* node is found, an event is fired. This allows any listeners to
|
949
|
+
* automatically be informed that this type of node has been found
|
950
|
+
* and react accordingly.
|
951
|
+
*/
|
952
|
+
traverser.traverse(sourceCode.ast, {
|
953
|
+
enter(node, parent) {
|
954
|
+
node.parent = parent;
|
955
|
+
eventGenerator.enterNode(node);
|
956
|
+
},
|
957
|
+
leave(node) {
|
958
|
+
eventGenerator.leaveNode(node);
|
988
959
|
}
|
989
960
|
});
|
990
961
|
|
991
962
|
return applyDisableDirectives({
|
992
963
|
directives: disableDirectives,
|
993
|
-
problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column)
|
964
|
+
problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
|
965
|
+
reportUnusedDisableDirectives
|
994
966
|
});
|
995
967
|
}
|
996
968
|
|
@@ -1028,7 +1000,7 @@ module.exports = class Linter {
|
|
1028
1000
|
* @returns {SourceCode} The SourceCode object.
|
1029
1001
|
*/
|
1030
1002
|
getSourceCode() {
|
1031
|
-
return this
|
1003
|
+
return lastSourceCodes.get(this);
|
1032
1004
|
}
|
1033
1005
|
|
1034
1006
|
/**
|
package/lib/logging.js
CHANGED
@@ -15,7 +15,7 @@ module.exports = {
|
|
15
15
|
* @returns {void}
|
16
16
|
*/
|
17
17
|
info() {
|
18
|
-
console.log.apply(console,
|
18
|
+
console.log.apply(console, arguments);
|
19
19
|
},
|
20
20
|
|
21
21
|
/**
|
@@ -23,6 +23,6 @@ module.exports = {
|
|
23
23
|
* @returns {void}
|
24
24
|
*/
|
25
25
|
error() {
|
26
|
-
console.error.apply(console,
|
26
|
+
console.error.apply(console, arguments);
|
27
27
|
}
|
28
28
|
};
|
package/lib/options.js
CHANGED
@@ -190,6 +190,12 @@ module.exports = optionator({
|
|
190
190
|
default: false,
|
191
191
|
description: "Automatically fix problems"
|
192
192
|
},
|
193
|
+
{
|
194
|
+
option: "fix-dry-run",
|
195
|
+
type: "Boolean",
|
196
|
+
default: false,
|
197
|
+
description: "Automatically fix problems without saving the changes to the file system"
|
198
|
+
},
|
193
199
|
{
|
194
200
|
option: "debug",
|
195
201
|
type: "Boolean",
|
@@ -214,6 +220,12 @@ module.exports = optionator({
|
|
214
220
|
default: "true",
|
215
221
|
description: "Prevent comments from changing config or rules"
|
216
222
|
},
|
223
|
+
{
|
224
|
+
option: "report-unused-disable-directives",
|
225
|
+
type: "Boolean",
|
226
|
+
default: false,
|
227
|
+
description: "Adds reported errors for unused eslint-disable directives"
|
228
|
+
},
|
217
229
|
{
|
218
230
|
option: "print-config",
|
219
231
|
type: "path::String",
|
@@ -23,7 +23,7 @@ module.exports = {
|
|
23
23
|
{
|
24
24
|
oneOf: [
|
25
25
|
{
|
26
|
-
enum: ["always", "never"]
|
26
|
+
enum: ["always", "never", "consistent"]
|
27
27
|
},
|
28
28
|
{
|
29
29
|
type: "object",
|
@@ -58,11 +58,15 @@ module.exports = {
|
|
58
58
|
* @returns {{multiline: boolean, minItems: number}} Normalized option object.
|
59
59
|
*/
|
60
60
|
function normalizeOptionValue(option) {
|
61
|
+
let consistent = false;
|
61
62
|
let multiline = false;
|
62
63
|
let minItems = 0;
|
63
64
|
|
64
65
|
if (option) {
|
65
|
-
if (option === "
|
66
|
+
if (option === "consistent") {
|
67
|
+
consistent = true;
|
68
|
+
minItems = Number.POSITIVE_INFINITY;
|
69
|
+
} else if (option === "always" || option.minItems === 0) {
|
66
70
|
minItems = 0;
|
67
71
|
} else if (option === "never") {
|
68
72
|
minItems = Number.POSITIVE_INFINITY;
|
@@ -71,11 +75,12 @@ module.exports = {
|
|
71
75
|
minItems = option.minItems || Number.POSITIVE_INFINITY;
|
72
76
|
}
|
73
77
|
} else {
|
78
|
+
consistent = false;
|
74
79
|
multiline = true;
|
75
80
|
minItems = Number.POSITIVE_INFINITY;
|
76
81
|
}
|
77
82
|
|
78
|
-
return { multiline, minItems };
|
83
|
+
return { consistent, multiline, minItems };
|
79
84
|
}
|
80
85
|
|
81
86
|
/**
|
@@ -173,8 +178,7 @@ module.exports = {
|
|
173
178
|
/**
|
174
179
|
* Reports a given node if it violated this rule.
|
175
180
|
*
|
176
|
-
* @param {ASTNode} node - A node to check. This is an
|
177
|
-
* @param {{multiline: boolean, minItems: number}} options - An option object.
|
181
|
+
* @param {ASTNode} node - A node to check. This is an ArrayExpression node or an ArrayPattern node.
|
178
182
|
* @returns {void}
|
179
183
|
*/
|
180
184
|
function check(node) {
|
@@ -194,6 +198,16 @@ module.exports = {
|
|
194
198
|
options.multiline &&
|
195
199
|
elements.length > 0 &&
|
196
200
|
firstIncComment.loc.start.line !== lastIncComment.loc.end.line
|
201
|
+
) ||
|
202
|
+
(
|
203
|
+
elements.length === 0 &&
|
204
|
+
firstIncComment.type === "Block" &&
|
205
|
+
firstIncComment.loc.start.line !== lastIncComment.loc.end.line &&
|
206
|
+
firstIncComment === lastIncComment
|
207
|
+
) ||
|
208
|
+
(
|
209
|
+
options.consistent &&
|
210
|
+
firstIncComment.loc.start.line !== openBracket.loc.end.line
|
197
211
|
)
|
198
212
|
);
|
199
213
|
|
@@ -14,7 +14,7 @@ const util = require("../ast-utils");
|
|
14
14
|
module.exports = {
|
15
15
|
meta: {
|
16
16
|
docs: {
|
17
|
-
description: "enforce
|
17
|
+
description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
|
18
18
|
category: "Stylistic Issues",
|
19
19
|
recommended: false
|
20
20
|
},
|
@@ -60,7 +60,8 @@ module.exports = {
|
|
60
60
|
if (node.type === "MemberExpression") {
|
61
61
|
if (node.object.type === "Identifier") {
|
62
62
|
return true;
|
63
|
-
}
|
63
|
+
}
|
64
|
+
if (node.object.type === "MemberExpression") {
|
64
65
|
return containsOnlyIdentifiers(node.object);
|
65
66
|
}
|
66
67
|
}
|
package/lib/rules/comma-style.js
CHANGED
@@ -208,7 +208,9 @@ module.exports = {
|
|
208
208
|
if (item) {
|
209
209
|
const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken);
|
210
210
|
|
211
|
-
previousItemToken = tokenAfterItem
|
211
|
+
previousItemToken = tokenAfterItem
|
212
|
+
? sourceCode.getTokenBefore(tokenAfterItem)
|
213
|
+
: sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
|
212
214
|
}
|
213
215
|
});
|
214
216
|
|
@@ -54,46 +54,67 @@ module.exports = {
|
|
54
54
|
allowPattern = new RegExp(options.allowPattern);
|
55
55
|
}
|
56
56
|
|
57
|
+
/**
|
58
|
+
* Check if the property is valid dot notation
|
59
|
+
* @param {ASTNode} node The dot notation node
|
60
|
+
* @param {string} value Value which is to be checked
|
61
|
+
* @returns {void}
|
62
|
+
*/
|
63
|
+
function checkComputedProperty(node, value) {
|
64
|
+
if (
|
65
|
+
validIdentifier.test(value) &&
|
66
|
+
(allowKeywords || keywords.indexOf(String(value)) === -1) &&
|
67
|
+
!(allowPattern && allowPattern.test(value))
|
68
|
+
) {
|
69
|
+
const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
|
70
|
+
|
71
|
+
context.report({
|
72
|
+
node: node.property,
|
73
|
+
message: "[{{propertyValue}}] is better written in dot notation.",
|
74
|
+
data: {
|
75
|
+
propertyValue: formattedValue
|
76
|
+
},
|
77
|
+
fix(fixer) {
|
78
|
+
const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
|
79
|
+
const rightBracket = sourceCode.getLastToken(node);
|
80
|
+
|
81
|
+
if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
|
82
|
+
|
83
|
+
// Don't perform any fixes if there are comments inside the brackets.
|
84
|
+
return null;
|
85
|
+
}
|
86
|
+
|
87
|
+
const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
|
88
|
+
const needsSpaceAfterProperty = tokenAfterProperty &&
|
89
|
+
rightBracket.range[1] === tokenAfterProperty.range[0] &&
|
90
|
+
!astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty);
|
91
|
+
|
92
|
+
const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
|
93
|
+
const textAfterProperty = needsSpaceAfterProperty ? " " : "";
|
94
|
+
|
95
|
+
return fixer.replaceTextRange(
|
96
|
+
[leftBracket.range[0], rightBracket.range[1]],
|
97
|
+
`${textBeforeDot}.${value}${textAfterProperty}`
|
98
|
+
);
|
99
|
+
}
|
100
|
+
});
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
57
104
|
return {
|
58
105
|
MemberExpression(node) {
|
59
106
|
if (
|
60
107
|
node.computed &&
|
61
|
-
node.property.type === "Literal"
|
62
|
-
validIdentifier.test(node.property.value) &&
|
63
|
-
(allowKeywords || keywords.indexOf(String(node.property.value)) === -1)
|
108
|
+
node.property.type === "Literal"
|
64
109
|
) {
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
|
74
|
-
const rightBracket = sourceCode.getLastToken(node);
|
75
|
-
|
76
|
-
if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
|
77
|
-
|
78
|
-
// Don't perform any fixes if there are comments inside the brackets.
|
79
|
-
return null;
|
80
|
-
}
|
81
|
-
|
82
|
-
const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
|
83
|
-
const needsSpaceAfterProperty = tokenAfterProperty &&
|
84
|
-
rightBracket.range[1] === tokenAfterProperty.range[0] &&
|
85
|
-
!astUtils.canTokensBeAdjacent(String(node.property.value), tokenAfterProperty);
|
86
|
-
|
87
|
-
const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
|
88
|
-
const textAfterProperty = needsSpaceAfterProperty ? " " : "";
|
89
|
-
|
90
|
-
return fixer.replaceTextRange(
|
91
|
-
[leftBracket.range[0], rightBracket.range[1]],
|
92
|
-
`${textBeforeDot}.${node.property.value}${textAfterProperty}`
|
93
|
-
);
|
94
|
-
}
|
95
|
-
});
|
96
|
-
}
|
110
|
+
checkComputedProperty(node, node.property.value);
|
111
|
+
}
|
112
|
+
if (
|
113
|
+
node.computed &&
|
114
|
+
node.property.type === "TemplateLiteral" &&
|
115
|
+
node.property.expressions.length === 0
|
116
|
+
) {
|
117
|
+
checkComputedProperty(node, node.property.quasis[0].value.cooked);
|
97
118
|
}
|
98
119
|
if (
|
99
120
|
!allowKeywords &&
|
@@ -68,7 +68,7 @@ module.exports = {
|
|
68
68
|
|
69
69
|
/**
|
70
70
|
* Returns resolved option definitions based on an option and defaults
|
71
|
-
*
|
71
|
+
*
|
72
72
|
* @param {any} option - The option object or string value
|
73
73
|
* @param {Object} defaults - The defaults to use if options are not present
|
74
74
|
* @returns {Object} the resolved object definition
|
@@ -121,7 +121,7 @@ module.exports = {
|
|
121
121
|
|
122
122
|
/**
|
123
123
|
* Checks the spacing between two tokens before or after the star token.
|
124
|
-
*
|
124
|
+
*
|
125
125
|
* @param {string} kind Either "named", "anonymous", or "method"
|
126
126
|
* @param {string} side Either "before" or "after".
|
127
127
|
* @param {Token} leftToken `function` keyword token if side is "before", or
|
@@ -161,7 +161,7 @@ module.exports = {
|
|
161
161
|
|
162
162
|
/**
|
163
163
|
* Enforces the spacing around the star if node is a generator function.
|
164
|
-
*
|
164
|
+
*
|
165
165
|
* @param {ASTNode} node A function expression or declaration node.
|
166
166
|
* @returns {void}
|
167
167
|
*/
|
@@ -733,7 +733,9 @@ module.exports = {
|
|
733
733
|
} else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
|
734
734
|
const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;
|
735
735
|
|
736
|
-
if (parentElements[0] &&
|
736
|
+
if (parentElements[0] &&
|
737
|
+
parentElements[0].loc.start.line === parent.loc.start.line &&
|
738
|
+
parentElements[0].loc.end.line !== parent.loc.start.line) {
|
737
739
|
|
738
740
|
/*
|
739
741
|
* If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
|
@@ -797,7 +799,8 @@ module.exports = {
|
|
797
799
|
}
|
798
800
|
}
|
799
801
|
|
800
|
-
checkLastNodeLineIndent(node, nodeIndent +
|
802
|
+
checkLastNodeLineIndent(node, nodeIndent +
|
803
|
+
(isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
|
801
804
|
}
|
802
805
|
|
803
806
|
/**
|