eslint 9.3.0 → 9.5.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/README.md +2 -2
- package/lib/api.js +1 -1
- package/lib/cli.js +2 -2
- package/lib/config/default-config.js +5 -0
- package/lib/config/flat-config-array.js +72 -9
- package/lib/config/flat-config-schema.js +46 -62
- package/lib/eslint/eslint-helpers.js +33 -23
- package/lib/eslint/eslint.js +15 -20
- package/lib/languages/js/index.js +247 -0
- package/lib/{source-code → languages/js/source-code}/source-code.js +38 -18
- package/lib/languages/js/validate-language-options.js +181 -0
- package/lib/linter/apply-disable-directives.js +8 -3
- package/lib/linter/linter.js +122 -121
- package/lib/linter/report-translator.js +14 -7
- package/lib/linter/vfile.js +104 -0
- package/lib/rule-tester/rule-tester.js +6 -3
- package/lib/rules/func-style.js +4 -4
- package/lib/rules/no-constructor-return.js +1 -1
- package/lib/rules/no-loop-func.js +161 -129
- package/lib/rules/no-sparse-arrays.js +26 -3
- package/messages/all-matched-files-ignored.js +21 -0
- package/package.json +11 -11
- /package/lib/{source-code → languages/js/source-code}/index.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-comment-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/cursors.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-comment-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/index.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/padded-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/utils.js +0 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview JavaScript Language Object
|
3
|
+
* @author Nicholas C. Zakas
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//-----------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//-----------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const { SourceCode } = require("./source-code");
|
13
|
+
const createDebug = require("debug");
|
14
|
+
const astUtils = require("../../shared/ast-utils");
|
15
|
+
const eslintScope = require("eslint-scope");
|
16
|
+
const evk = require("eslint-visitor-keys");
|
17
|
+
const { validateLanguageOptions } = require("./validate-language-options");
|
18
|
+
|
19
|
+
//-----------------------------------------------------------------------------
|
20
|
+
// Type Definitions
|
21
|
+
//-----------------------------------------------------------------------------
|
22
|
+
|
23
|
+
/** @typedef {import("../../linter/vfile").VFile} VFile */
|
24
|
+
|
25
|
+
//-----------------------------------------------------------------------------
|
26
|
+
// Helpers
|
27
|
+
//-----------------------------------------------------------------------------
|
28
|
+
|
29
|
+
const debug = createDebug("eslint:languages:js");
|
30
|
+
const DEFAULT_ECMA_VERSION = 5;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Analyze scope of the given AST.
|
34
|
+
* @param {ASTNode} ast The `Program` node to analyze.
|
35
|
+
* @param {LanguageOptions} languageOptions The parser options.
|
36
|
+
* @param {Record<string, string[]>} visitorKeys The visitor keys.
|
37
|
+
* @returns {ScopeManager} The analysis result.
|
38
|
+
*/
|
39
|
+
function analyzeScope(ast, languageOptions, visitorKeys) {
|
40
|
+
const parserOptions = languageOptions.parserOptions;
|
41
|
+
const ecmaFeatures = parserOptions.ecmaFeatures || {};
|
42
|
+
const ecmaVersion = languageOptions.ecmaVersion || DEFAULT_ECMA_VERSION;
|
43
|
+
|
44
|
+
return eslintScope.analyze(ast, {
|
45
|
+
ignoreEval: true,
|
46
|
+
nodejsScope: ecmaFeatures.globalReturn,
|
47
|
+
impliedStrict: ecmaFeatures.impliedStrict,
|
48
|
+
ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 6,
|
49
|
+
sourceType: languageOptions.sourceType || "script",
|
50
|
+
childVisitorKeys: visitorKeys || evk.KEYS,
|
51
|
+
fallback: evk.getKeys
|
52
|
+
});
|
53
|
+
}
|
54
|
+
|
55
|
+
//-----------------------------------------------------------------------------
|
56
|
+
// Exports
|
57
|
+
//-----------------------------------------------------------------------------
|
58
|
+
|
59
|
+
module.exports = {
|
60
|
+
|
61
|
+
fileType: "text",
|
62
|
+
lineStart: 1,
|
63
|
+
columnStart: 0,
|
64
|
+
nodeTypeKey: "type",
|
65
|
+
visitorKeys: evk.KEYS,
|
66
|
+
|
67
|
+
validateLanguageOptions,
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Determines if a given node matches a given selector class.
|
71
|
+
* @param {string} className The class name to check.
|
72
|
+
* @param {ASTNode} node The node to check.
|
73
|
+
* @param {Array<ASTNode>} ancestry The ancestry of the node.
|
74
|
+
* @returns {boolean} True if there's a match, false if not.
|
75
|
+
* @throws {Error} When an unknown class name is passed.
|
76
|
+
*/
|
77
|
+
matchesSelectorClass(className, node, ancestry) {
|
78
|
+
|
79
|
+
/*
|
80
|
+
* Copyright (c) 2013, Joel Feenstra
|
81
|
+
* All rights reserved.
|
82
|
+
*
|
83
|
+
* Redistribution and use in source and binary forms, with or without
|
84
|
+
* modification, are permitted provided that the following conditions are met:
|
85
|
+
* * Redistributions of source code must retain the above copyright
|
86
|
+
* notice, this list of conditions and the following disclaimer.
|
87
|
+
* * Redistributions in binary form must reproduce the above copyright
|
88
|
+
* notice, this list of conditions and the following disclaimer in the
|
89
|
+
* documentation and/or other materials provided with the distribution.
|
90
|
+
* * Neither the name of the ESQuery nor the names of its contributors may
|
91
|
+
* be used to endorse or promote products derived from this software without
|
92
|
+
* specific prior written permission.
|
93
|
+
*
|
94
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
95
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
96
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
97
|
+
* DISCLAIMED. IN NO EVENT SHALL JOEL FEENSTRA BE LIABLE FOR ANY
|
98
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
99
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
100
|
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
101
|
+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
102
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
103
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
104
|
+
*/
|
105
|
+
|
106
|
+
switch (className.toLowerCase()) {
|
107
|
+
|
108
|
+
case "statement":
|
109
|
+
if (node.type.slice(-9) === "Statement") {
|
110
|
+
return true;
|
111
|
+
}
|
112
|
+
|
113
|
+
// fallthrough: interface Declaration <: Statement { }
|
114
|
+
|
115
|
+
case "declaration":
|
116
|
+
return node.type.slice(-11) === "Declaration";
|
117
|
+
|
118
|
+
case "pattern":
|
119
|
+
if (node.type.slice(-7) === "Pattern") {
|
120
|
+
return true;
|
121
|
+
}
|
122
|
+
|
123
|
+
// fallthrough: interface Expression <: Node, Pattern { }
|
124
|
+
|
125
|
+
case "expression":
|
126
|
+
return node.type.slice(-10) === "Expression" ||
|
127
|
+
node.type.slice(-7) === "Literal" ||
|
128
|
+
(
|
129
|
+
node.type === "Identifier" &&
|
130
|
+
(ancestry.length === 0 || ancestry[0].type !== "MetaProperty")
|
131
|
+
) ||
|
132
|
+
node.type === "MetaProperty";
|
133
|
+
|
134
|
+
case "function":
|
135
|
+
return node.type === "FunctionDeclaration" ||
|
136
|
+
node.type === "FunctionExpression" ||
|
137
|
+
node.type === "ArrowFunctionExpression";
|
138
|
+
|
139
|
+
default:
|
140
|
+
throw new Error(`Unknown class name: ${className}`);
|
141
|
+
}
|
142
|
+
},
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Parses the given file into an AST.
|
146
|
+
* @param {VFile} file The virtual file to parse.
|
147
|
+
* @param {Object} options Additional options passed from ESLint.
|
148
|
+
* @param {LanguageOptions} options.languageOptions The language options.
|
149
|
+
* @returns {Object} The result of parsing.
|
150
|
+
*/
|
151
|
+
parse(file, { languageOptions }) {
|
152
|
+
|
153
|
+
// Note: BOM already removed
|
154
|
+
const { body: text, path: filePath } = file;
|
155
|
+
const textToParse = text.replace(astUtils.shebangPattern, (match, captured) => `//${captured}`);
|
156
|
+
const { ecmaVersion, sourceType, parser } = languageOptions;
|
157
|
+
const parserOptions = Object.assign(
|
158
|
+
{ ecmaVersion, sourceType },
|
159
|
+
languageOptions.parserOptions,
|
160
|
+
{
|
161
|
+
loc: true,
|
162
|
+
range: true,
|
163
|
+
raw: true,
|
164
|
+
tokens: true,
|
165
|
+
comment: true,
|
166
|
+
eslintVisitorKeys: true,
|
167
|
+
eslintScopeManager: true,
|
168
|
+
filePath
|
169
|
+
}
|
170
|
+
);
|
171
|
+
|
172
|
+
/*
|
173
|
+
* Check for parsing errors first. If there's a parsing error, nothing
|
174
|
+
* else can happen. However, a parsing error does not throw an error
|
175
|
+
* from this method - it's just considered a fatal error message, a
|
176
|
+
* problem that ESLint identified just like any other.
|
177
|
+
*/
|
178
|
+
try {
|
179
|
+
debug("Parsing:", filePath);
|
180
|
+
const parseResult = (typeof parser.parseForESLint === "function")
|
181
|
+
? parser.parseForESLint(textToParse, parserOptions)
|
182
|
+
: { ast: parser.parse(textToParse, parserOptions) };
|
183
|
+
|
184
|
+
debug("Parsing successful:", filePath);
|
185
|
+
|
186
|
+
const {
|
187
|
+
ast,
|
188
|
+
services: parserServices = {},
|
189
|
+
visitorKeys = evk.KEYS,
|
190
|
+
scopeManager
|
191
|
+
} = parseResult;
|
192
|
+
|
193
|
+
return {
|
194
|
+
ok: true,
|
195
|
+
ast,
|
196
|
+
parserServices,
|
197
|
+
visitorKeys,
|
198
|
+
scopeManager
|
199
|
+
};
|
200
|
+
} catch (ex) {
|
201
|
+
|
202
|
+
// If the message includes a leading line number, strip it:
|
203
|
+
const message = `Parsing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
|
204
|
+
|
205
|
+
debug("%s\n%s", message, ex.stack);
|
206
|
+
|
207
|
+
return {
|
208
|
+
ok: false,
|
209
|
+
errors: [{
|
210
|
+
message,
|
211
|
+
line: ex.lineNumber,
|
212
|
+
column: ex.column
|
213
|
+
}]
|
214
|
+
};
|
215
|
+
}
|
216
|
+
|
217
|
+
},
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Creates a new `SourceCode` object from the given information.
|
221
|
+
* @param {VFile} file The virtual file to create a `SourceCode` object from.
|
222
|
+
* @param {Object} parseResult The result returned from `parse()`.
|
223
|
+
* @param {Object} options Additional options passed from ESLint.
|
224
|
+
* @param {LanguageOptions} options.languageOptions The language options.
|
225
|
+
* @returns {SourceCode} The new `SourceCode` object.
|
226
|
+
*/
|
227
|
+
createSourceCode(file, parseResult, { languageOptions }) {
|
228
|
+
|
229
|
+
const { body: text, path: filePath, bom: hasBOM } = file;
|
230
|
+
const { ast, parserServices, visitorKeys } = parseResult;
|
231
|
+
|
232
|
+
debug("Scope analysis:", filePath);
|
233
|
+
const scopeManager = parseResult.scopeManager || analyzeScope(ast, languageOptions, visitorKeys);
|
234
|
+
|
235
|
+
debug("Scope analysis successful:", filePath);
|
236
|
+
|
237
|
+
return new SourceCode({
|
238
|
+
text,
|
239
|
+
ast,
|
240
|
+
hasBOM,
|
241
|
+
parserServices,
|
242
|
+
scopeManager,
|
243
|
+
visitorKeys
|
244
|
+
});
|
245
|
+
}
|
246
|
+
|
247
|
+
};
|
@@ -11,18 +11,16 @@
|
|
11
11
|
const
|
12
12
|
{ isCommentToken } = require("@eslint-community/eslint-utils"),
|
13
13
|
TokenStore = require("./token-store"),
|
14
|
-
astUtils = require("
|
15
|
-
Traverser = require("
|
16
|
-
globals = require("
|
14
|
+
astUtils = require("../../../shared/ast-utils"),
|
15
|
+
Traverser = require("../../../shared/traverser"),
|
16
|
+
globals = require("../../../../conf/globals"),
|
17
17
|
{
|
18
18
|
directivesPattern
|
19
|
-
} = require("
|
19
|
+
} = require("../../../shared/directives"),
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
ConfigCommentParser = require("../linter/config-comment-parser"),
|
25
|
-
/* eslint-enable n/no-restricted-require -- Should eventually be moved into SourceCode. */
|
21
|
+
CodePathAnalyzer = require("../../../linter/code-path-analysis/code-path-analyzer"),
|
22
|
+
createEmitter = require("../../../linter/safe-emitter"),
|
23
|
+
ConfigCommentParser = require("../../../linter/config-comment-parser"),
|
26
24
|
|
27
25
|
eslintScope = require("eslint-scope");
|
28
26
|
|
@@ -441,24 +439,28 @@ class SourceCode extends TokenStore {
|
|
441
439
|
#steps;
|
442
440
|
|
443
441
|
/**
|
442
|
+
* Creates a new instance.
|
444
443
|
* @param {string|Object} textOrConfig The source code text or config object.
|
445
444
|
* @param {string} textOrConfig.text The source code text.
|
446
445
|
* @param {ASTNode} textOrConfig.ast The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
|
446
|
+
* @param {boolean} textOrConfig.hasBOM Indicates if the text has a Unicode BOM.
|
447
447
|
* @param {Object|null} textOrConfig.parserServices The parser services.
|
448
448
|
* @param {ScopeManager|null} textOrConfig.scopeManager The scope of this source code.
|
449
449
|
* @param {Object|null} textOrConfig.visitorKeys The visitor keys to traverse AST.
|
450
450
|
* @param {ASTNode} [astIfNoConfig] The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
|
451
451
|
*/
|
452
452
|
constructor(textOrConfig, astIfNoConfig) {
|
453
|
-
let text, ast, parserServices, scopeManager, visitorKeys;
|
453
|
+
let text, hasBOM, ast, parserServices, scopeManager, visitorKeys;
|
454
454
|
|
455
|
-
// Process overloading
|
455
|
+
// Process overloading of arguments
|
456
456
|
if (typeof textOrConfig === "string") {
|
457
457
|
text = textOrConfig;
|
458
458
|
ast = astIfNoConfig;
|
459
|
+
hasBOM = false;
|
459
460
|
} else if (typeof textOrConfig === "object" && textOrConfig !== null) {
|
460
461
|
text = textOrConfig.text;
|
461
462
|
ast = textOrConfig.ast;
|
463
|
+
hasBOM = textOrConfig.hasBOM;
|
462
464
|
parserServices = textOrConfig.parserServices;
|
463
465
|
scopeManager = textOrConfig.scopeManager;
|
464
466
|
visitorKeys = textOrConfig.visitorKeys;
|
@@ -476,18 +478,39 @@ class SourceCode extends TokenStore {
|
|
476
478
|
["configNodes", void 0]
|
477
479
|
]);
|
478
480
|
|
481
|
+
/**
|
482
|
+
* Indicates if the AST is ESTree compatible.
|
483
|
+
* @type {boolean}
|
484
|
+
*/
|
485
|
+
this.isESTree = ast.type === "Program";
|
486
|
+
|
487
|
+
/*
|
488
|
+
* Backwards compatibility for BOM handling.
|
489
|
+
*
|
490
|
+
* The `hasBOM` property has been available on the `SourceCode` object
|
491
|
+
* for a long time and is used to indicate if the source contains a BOM.
|
492
|
+
* The linter strips the BOM and just passes the `hasBOM` property to the
|
493
|
+
* `SourceCode` constructor to make it easier for languages to not deal with
|
494
|
+
* the BOM.
|
495
|
+
*
|
496
|
+
* However, the text passed in to the `SourceCode` constructor might still
|
497
|
+
* have a BOM if the constructor is called outside of the linter, so we still
|
498
|
+
* need to check for the BOM in the text.
|
499
|
+
*/
|
500
|
+
const textHasBOM = text.charCodeAt(0) === 0xFEFF;
|
501
|
+
|
479
502
|
/**
|
480
503
|
* The flag to indicate that the source code has Unicode BOM.
|
481
504
|
* @type {boolean}
|
482
505
|
*/
|
483
|
-
this.hasBOM =
|
506
|
+
this.hasBOM = textHasBOM || !!hasBOM;
|
484
507
|
|
485
508
|
/**
|
486
509
|
* The original text source code.
|
487
510
|
* BOM was stripped from this text.
|
488
511
|
* @type {string}
|
489
512
|
*/
|
490
|
-
this.text = (
|
513
|
+
this.text = (textHasBOM ? text.slice(1) : text);
|
491
514
|
|
492
515
|
/**
|
493
516
|
* The parsed AST for the source code.
|
@@ -1164,12 +1187,11 @@ class SourceCode extends TokenStore {
|
|
1164
1187
|
*/
|
1165
1188
|
finalize() {
|
1166
1189
|
|
1167
|
-
// Step 1: ensure that all of the necessary variables are up to date
|
1168
1190
|
const varsCache = this[caches].get("vars");
|
1169
|
-
const globalScope = this.scopeManager.scopes[0];
|
1170
1191
|
const configGlobals = varsCache.get("configGlobals");
|
1171
1192
|
const inlineGlobals = varsCache.get("inlineGlobals");
|
1172
1193
|
const exportedVariables = varsCache.get("exportedVariables");
|
1194
|
+
const globalScope = this.scopeManager.scopes[0];
|
1173
1195
|
|
1174
1196
|
addDeclaredGlobals(globalScope, configGlobals, inlineGlobals);
|
1175
1197
|
|
@@ -1227,9 +1249,7 @@ class SourceCode extends TokenStore {
|
|
1227
1249
|
* Program node at the top level. This is not a perfect heuristic, but it
|
1228
1250
|
* is good enough for now.
|
1229
1251
|
*/
|
1230
|
-
|
1231
|
-
|
1232
|
-
if (isESTree) {
|
1252
|
+
if (this.isESTree) {
|
1233
1253
|
analyzer = new CodePathAnalyzer(analyzer);
|
1234
1254
|
|
1235
1255
|
CODE_PATH_EVENTS.forEach(eventName => {
|
@@ -0,0 +1,181 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview The schema to validate language options
|
3
|
+
* @author Nicholas C. Zakas
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//-----------------------------------------------------------------------------
|
9
|
+
// Data
|
10
|
+
//-----------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const globalVariablesValues = new Set([
|
13
|
+
true, "true", "writable", "writeable",
|
14
|
+
false, "false", "readonly", "readable", null,
|
15
|
+
"off"
|
16
|
+
]);
|
17
|
+
|
18
|
+
//------------------------------------------------------------------------------
|
19
|
+
// Helpers
|
20
|
+
//------------------------------------------------------------------------------
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Check if a value is a non-null object.
|
24
|
+
* @param {any} value The value to check.
|
25
|
+
* @returns {boolean} `true` if the value is a non-null object.
|
26
|
+
*/
|
27
|
+
function isNonNullObject(value) {
|
28
|
+
return typeof value === "object" && value !== null;
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Check if a value is a non-null non-array object.
|
33
|
+
* @param {any} value The value to check.
|
34
|
+
* @returns {boolean} `true` if the value is a non-null non-array object.
|
35
|
+
*/
|
36
|
+
function isNonArrayObject(value) {
|
37
|
+
return isNonNullObject(value) && !Array.isArray(value);
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Check if a value is undefined.
|
42
|
+
* @param {any} value The value to check.
|
43
|
+
* @returns {boolean} `true` if the value is undefined.
|
44
|
+
*/
|
45
|
+
function isUndefined(value) {
|
46
|
+
return typeof value === "undefined";
|
47
|
+
}
|
48
|
+
|
49
|
+
//-----------------------------------------------------------------------------
|
50
|
+
// Schemas
|
51
|
+
//-----------------------------------------------------------------------------
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Validates the ecmaVersion property.
|
55
|
+
* @param {string|number} ecmaVersion The value to check.
|
56
|
+
* @returns {void}
|
57
|
+
* @throws {TypeError} If the value is invalid.
|
58
|
+
*/
|
59
|
+
function validateEcmaVersion(ecmaVersion) {
|
60
|
+
|
61
|
+
if (isUndefined(ecmaVersion)) {
|
62
|
+
throw new TypeError("Key \"ecmaVersion\": Expected an \"ecmaVersion\" property.");
|
63
|
+
}
|
64
|
+
|
65
|
+
if (typeof ecmaVersion !== "number" && ecmaVersion !== "latest") {
|
66
|
+
throw new TypeError("Key \"ecmaVersion\": Expected a number or \"latest\".");
|
67
|
+
}
|
68
|
+
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Validates the sourceType property.
|
73
|
+
* @param {string} sourceType The value to check.
|
74
|
+
* @returns {void}
|
75
|
+
* @throws {TypeError} If the value is invalid.
|
76
|
+
*/
|
77
|
+
function validateSourceType(sourceType) {
|
78
|
+
|
79
|
+
if (typeof sourceType !== "string" || !/^(?:script|module|commonjs)$/u.test(sourceType)) {
|
80
|
+
throw new TypeError("Key \"sourceType\": Expected \"script\", \"module\", or \"commonjs\".");
|
81
|
+
}
|
82
|
+
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Validates the globals property.
|
87
|
+
* @param {Object} globals The value to check.
|
88
|
+
* @returns {void}
|
89
|
+
* @throws {TypeError} If the value is invalid.
|
90
|
+
*/
|
91
|
+
function validateGlobals(globals) {
|
92
|
+
|
93
|
+
if (!isNonArrayObject(globals)) {
|
94
|
+
throw new TypeError("Key \"globals\": Expected an object.");
|
95
|
+
}
|
96
|
+
|
97
|
+
for (const key of Object.keys(globals)) {
|
98
|
+
|
99
|
+
// avoid hairy edge case
|
100
|
+
if (key === "__proto__") {
|
101
|
+
continue;
|
102
|
+
}
|
103
|
+
|
104
|
+
if (key !== key.trim()) {
|
105
|
+
throw new TypeError(`Key "globals": Global "${key}" has leading or trailing whitespace.`);
|
106
|
+
}
|
107
|
+
|
108
|
+
if (!globalVariablesValues.has(globals[key])) {
|
109
|
+
throw new TypeError(`Key "globals": Key "${key}": Expected "readonly", "writable", or "off".`);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Validates the parser property.
|
116
|
+
* @param {Object} parser The value to check.
|
117
|
+
* @returns {void}
|
118
|
+
* @throws {TypeError} If the value is invalid.
|
119
|
+
*/
|
120
|
+
function validateParser(parser) {
|
121
|
+
|
122
|
+
if (!parser || typeof parser !== "object" ||
|
123
|
+
(typeof parser.parse !== "function" && typeof parser.parseForESLint !== "function")
|
124
|
+
) {
|
125
|
+
throw new TypeError("Key \"parser\": Expected object with parse() or parseForESLint() method.");
|
126
|
+
}
|
127
|
+
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Validates the language options.
|
132
|
+
* @param {Object} languageOptions The language options to validate.
|
133
|
+
* @returns {void}
|
134
|
+
* @throws {TypeError} If the language options are invalid.
|
135
|
+
*/
|
136
|
+
function validateLanguageOptions(languageOptions) {
|
137
|
+
|
138
|
+
if (!isNonArrayObject(languageOptions)) {
|
139
|
+
throw new TypeError("Expected an object.");
|
140
|
+
}
|
141
|
+
|
142
|
+
const {
|
143
|
+
ecmaVersion,
|
144
|
+
sourceType,
|
145
|
+
globals,
|
146
|
+
parser,
|
147
|
+
parserOptions,
|
148
|
+
...otherOptions
|
149
|
+
} = languageOptions;
|
150
|
+
|
151
|
+
if ("ecmaVersion" in languageOptions) {
|
152
|
+
validateEcmaVersion(ecmaVersion);
|
153
|
+
}
|
154
|
+
|
155
|
+
if ("sourceType" in languageOptions) {
|
156
|
+
validateSourceType(sourceType);
|
157
|
+
}
|
158
|
+
|
159
|
+
if ("globals" in languageOptions) {
|
160
|
+
validateGlobals(globals);
|
161
|
+
}
|
162
|
+
|
163
|
+
if ("parser" in languageOptions) {
|
164
|
+
validateParser(parser);
|
165
|
+
}
|
166
|
+
|
167
|
+
if ("parserOptions" in languageOptions) {
|
168
|
+
if (!isNonArrayObject(parserOptions)) {
|
169
|
+
throw new TypeError("Key \"parserOptions\": Expected an object.");
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
const otherOptionKeys = Object.keys(otherOptions);
|
174
|
+
|
175
|
+
if (otherOptionKeys.length > 0) {
|
176
|
+
throw new TypeError(`Unexpected key "${otherOptionKeys[0]}" found.`);
|
177
|
+
}
|
178
|
+
|
179
|
+
}
|
180
|
+
|
181
|
+
module.exports = { validateLanguageOptions };
|
@@ -369,6 +369,8 @@ function applyDirectives(options) {
|
|
369
369
|
|
370
370
|
const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
|
371
371
|
.concat(processUnusedDirectives(unusedEnableDirectivesToReport));
|
372
|
+
const columnOffset = options.language.columnStart === 1 ? 0 : 1;
|
373
|
+
const lineOffset = options.language.lineStart === 1 ? 0 : 1;
|
372
374
|
|
373
375
|
const unusedDirectives = processed
|
374
376
|
.map(({ description, fix, unprocessedDirective }) => {
|
@@ -388,8 +390,8 @@ function applyDirectives(options) {
|
|
388
390
|
return {
|
389
391
|
ruleId: null,
|
390
392
|
message,
|
391
|
-
line: type === "disable-next-line" ? parentDirective.node.loc.start.line : line,
|
392
|
-
column: type === "disable-next-line" ? parentDirective.node.loc.start.column +
|
393
|
+
line: type === "disable-next-line" ? parentDirective.node.loc.start.line + lineOffset : line,
|
394
|
+
column: type === "disable-next-line" ? parentDirective.node.loc.start.column + columnOffset : column,
|
393
395
|
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
394
396
|
nodeType: null,
|
395
397
|
...options.disableFixes ? {} : { fix }
|
@@ -403,6 +405,7 @@ function applyDirectives(options) {
|
|
403
405
|
* Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
|
404
406
|
* of reported problems, adds the suppression information to the problems.
|
405
407
|
* @param {Object} options Information about directives and problems
|
408
|
+
* @param {Language} options.language The language being linted.
|
406
409
|
* @param {{
|
407
410
|
* type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
|
408
411
|
* ruleId: (string|null),
|
@@ -421,7 +424,7 @@ function applyDirectives(options) {
|
|
421
424
|
* @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
|
422
425
|
* An object with a list of reported problems, the suppressed of which contain the suppression information.
|
423
426
|
*/
|
424
|
-
module.exports = ({ directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
|
427
|
+
module.exports = ({ language, directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
|
425
428
|
const blockDirectives = directives
|
426
429
|
.filter(directive => directive.type === "disable" || directive.type === "enable")
|
427
430
|
.map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
|
@@ -470,6 +473,7 @@ module.exports = ({ directives, disableFixes, problems, configuredRules, ruleFil
|
|
470
473
|
}
|
471
474
|
|
472
475
|
const blockDirectivesResult = applyDirectives({
|
476
|
+
language,
|
473
477
|
problems,
|
474
478
|
directives: blockDirectives,
|
475
479
|
disableFixes,
|
@@ -477,6 +481,7 @@ module.exports = ({ directives, disableFixes, problems, configuredRules, ruleFil
|
|
477
481
|
rulesToIgnore
|
478
482
|
});
|
479
483
|
const lineDirectivesResult = applyDirectives({
|
484
|
+
language,
|
480
485
|
problems: blockDirectivesResult.problems,
|
481
486
|
directives: lineDirectives,
|
482
487
|
disableFixes,
|