eslint 4.9.0 → 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 +61 -0
- package/README.md +13 -1
- package/lib/cli-engine.js +2 -2
- package/lib/cli.js +7 -5
- package/lib/rules/comma-style.js +3 -1
- package/lib/rules/generator-star-spacing.js +3 -3
- package/lib/rules/indent-legacy.js +5 -2
- package/lib/rules/indent.js +3 -1
- package/lib/rules/lines-around-comment.js +4 -1
- package/lib/rules/multiline-comment-style.js +21 -7
- package/lib/rules/no-catch-shadow.js +1 -1
- package/lib/rules/no-constant-condition.js +2 -2
- package/lib/rules/no-lonely-if.js +2 -1
- 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/sort-imports.js +2 -1
- package/lib/rules/valid-jsdoc.js +39 -33
- package/package.json +2 -3
- 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/CHANGELOG.md
CHANGED
@@ -1,3 +1,64 @@
|
|
1
|
+
v4.10.0 - October 27, 2017
|
2
|
+
|
3
|
+
* bb6e60a Fix: Improve the doc for no-restricted-modules rule (fixes #9437) (#9495) (vibss2397)
|
4
|
+
* c529de9 Docs: Amend rule document to correct and complete it (refs #6251). (#9498) (Jonathan Pool)
|
5
|
+
* f9c6673 Chore: Add tests to cover array and object values and leading commas. (#9502) (Jonathan Pool)
|
6
|
+
* 9169258 Chore: remove `npm run check-commit` script (#9513) (Teddy Katz)
|
7
|
+
* 7d390b2 Docs: Revise contributor documentation on issue labels. (#9469) (Jonathan Pool)
|
8
|
+
* d80b9d0 Fix: no-var don't fix globals (fixes #9520) (#9525) (Toru Nagashima)
|
9
|
+
* b8aa071 Fix: allow linting the empty string from stdin (fixes #9515) (#9517) (Teddy Katz)
|
10
|
+
* 350a72c Chore: regex.test => string.startsWith (#9518) (薛定谔的猫)
|
11
|
+
* de0bef4 Chore: remove obsolete eslintbot templates (#9512) (Teddy Katz)
|
12
|
+
* 720b6d5 Docs: Update ISSUE_TEMPLATE.md (#9504) (薛定谔的猫)
|
13
|
+
* 2fa64b7 Fix: should not convert non-consecutive line comments to a single blo… (#9475) (薛定谔的猫)
|
14
|
+
* 9725146 Fix: multiline-comment-style fix produces invalid code (fixes #9461). (#9463) (薛定谔的猫)
|
15
|
+
* b12cff8 Fix: Expected order of jsdoc tags (fixes #9412) (#9451) (Orlando Wenzinger)
|
16
|
+
* f054ab5 Docs: add `.md` to link (for github users) (#9501) (薛定谔的猫)
|
17
|
+
* 5ed9cfc Docs: Correct violations of “Variable Declarations” in Code Conventions (#9447) (Jonathan Pool)
|
18
|
+
* 3171097 Docs: Clears confusion on usage of global and local plugins.(#9492) (Vasili Sviridov)
|
19
|
+
* 3204773 Chore: enable max-len. (#9414) (薛定谔的猫)
|
20
|
+
* 0f71fef Docs: Unquote booleans in lines-between-class-members docs (#9497) (Brandon Mills)
|
21
|
+
* b3d7532 Docs: use consistent terminology & fix link etc. (#9490) (薛定谔的猫)
|
22
|
+
* 87db8ae Docs: Fix broken links (#9488) (gpiress)
|
23
|
+
* 51bdb2f Docs: Incorrect link to related rule (#9477) (Gavin King)
|
24
|
+
* 1a962e8 Docs: Add FAQ for when ESLint cannot find plugin (#9467) (Kevin Partington)
|
25
|
+
* 8768b2d Fix: multiline-comment-style autofixer added trailing space (#9454) (Teddy Katz)
|
26
|
+
* e830aa1 Fix: multiline-comment-style reports block comments followed by code (#9450) (Teddy Katz)
|
27
|
+
* b12e5fe Docs: Repair broken links and add migration links. (#9473) (Jonathan Pool)
|
28
|
+
* eca01ed Docs: Add missing info about special status of home-dir config files. (#9472) (Jonathan Pool)
|
29
|
+
* eb8cfb1 Fix: change err report in constant condition (fixes #9398) (#9436) (Victor Hom)
|
30
|
+
* da77eb4 Chore: Revise no-config-file test to prevent false failure. (#9443) (Jonathan Pool)
|
31
|
+
* 47e5f6f Docs: ensure "good commit message" examples actually follow guidelines (#9466) (Teddy Katz)
|
32
|
+
* ebb530d Update: Don't ignore comments (no-trailing-spaces) (#9416) (Chris van Marle)
|
33
|
+
* 5012661 Build: fix `npm run profile` script (fixes #9397) (#9455) (Teddy Katz)
|
34
|
+
* ecac0fd Docs: Remove blockBindings references (#9446) (Jan Pilzer)
|
35
|
+
* 0b89865 Chore: ensure tests for internal rules get run (#9453) (Teddy Katz)
|
36
|
+
* 052c504 Docs: suggest deleting branches after merging PRs (#9449) (Teddy Katz)
|
37
|
+
* b31e55a Chore: move internal rules out of lib/ (#9448) (Teddy Katz)
|
38
|
+
* a7521e3 Docs: improve examples for multiline-comment-style (#9440) (Teddy Katz)
|
39
|
+
* 235c7dd 4.9.0 (ESLint Jenkins)
|
40
|
+
* b6f31a9 Build: changelog update for 4.9.0 (ESLint Jenkins)
|
41
|
+
* 85388fb Fix: Correct error and test messages to fit config search path (#9428) (Jonathan Pool)
|
42
|
+
* 62a323c Fix: Add class options for `lines-around-comment` (fixes #8564) (#8565) (Ed Lee)
|
43
|
+
* 8eb4aae New: multiline-comment-style rule (fixes #8320) (#9389) (薛定谔的猫)
|
44
|
+
* db41408 Chore: avoid applying eslint-env comments twice (#9278) (Teddy Katz)
|
45
|
+
* febb897 Chore: avoid loose equality assertions (#9415) (Teddy Katz)
|
46
|
+
* 2247efa Update: Add FunctionExpression to require-jsdoc (fixes #5867) (#9395) (Kai Cataldo)
|
47
|
+
* 6791d18 Docs: Corrected noun to verb. (#9438) (Jonathan Pool)
|
48
|
+
* b02fbb6 Update: custom messages for no-restricted-* (refs #8400) (Maja Wichrowska)
|
49
|
+
* 02732bd Docs: Reorganized to avoid misunderstandings. (#9434) (Jonathan Pool)
|
50
|
+
* d9466b8 Docs: Correct time forecast for tests. (#9432) (Jonathan Pool)
|
51
|
+
* f7ed84f Docs: Add instruction re home-directory config files (refs #7729) (#9426) (Jonathan Pool)
|
52
|
+
* 30d018b Chore: Add Aladdin-ADD & VictorHom to README (#9424) (Kai Cataldo)
|
53
|
+
* 2d8a303 Docs: fix examples for prefer-numeric-literals (#9155) (Lutz Lengemann)
|
54
|
+
* d7610f5 Docs: Add jquery warning to prefer-destructuring (#9409) (Thomas Grainger)
|
55
|
+
* e835dd1 Docs: clarify no-mixed-operators (fixes #8051) (Ruxandra Fediuc)
|
56
|
+
* 51360c8 Docs: update block-spacing details (fixes #8743) (#9375) (Victor Hom)
|
57
|
+
* 6767857 Update: fix ignored nodes in indent rule when using tabs (fixes #9392) (#9393) (Robin Houston)
|
58
|
+
* 37dde77 Chore: Refactor SourceCode#getJSDocComment (#9403) (Kai Cataldo)
|
59
|
+
* 9fedd51 Chore: Add missing space in blog post template (#9407) (Kevin Partington)
|
60
|
+
* 7654c99 Docs: add installing prerequisites in readme. (#9401) (薛定谔的猫)
|
61
|
+
|
1
62
|
v4.9.0 - October 14, 2017
|
2
63
|
|
3
64
|
* 85388fb Fix: Correct error and test messages to fit config search path (#9428) (Jonathan Pool)
|
package/README.md
CHANGED
@@ -206,11 +206,23 @@ Maybe, depending on how much you need it. [JSCS has reached end of life](https:/
|
|
206
206
|
|
207
207
|
If you are having issues with JSCS, you can try to move to ESLint. We are focusing our time and energy on JSCS compatibility issues.
|
208
208
|
|
209
|
-
|
210
209
|
### Is ESLint just linting or does it also check style?
|
211
210
|
|
212
211
|
ESLint does both traditional linting (looking for problematic patterns) and style checking (enforcement of conventions). You can use it for both.
|
213
212
|
|
213
|
+
### Why can't ESLint find my plugins?
|
214
|
+
|
215
|
+
ESLint can be [globally or locally installed](#installation-and-usage). If you install ESLint globally, your plugins must also be installed globally; if you install ESLint locally, your plugins must also be installed locally.
|
216
|
+
|
217
|
+
If you are trying to run globally, make sure your plugins are installed globally (use `npm ls -g`).
|
218
|
+
|
219
|
+
If you are trying to run locally:
|
220
|
+
|
221
|
+
* Make sure your plugins (and ESLint) are both in your project's `package.json` as devDependencies (or dependencies, if your project uses ESLint at runtime).
|
222
|
+
* Make sure you have run `npm install` and all your dependencies are installed.
|
223
|
+
|
224
|
+
In all cases, make sure your plugins' peerDependencies have been installed as well. You can use `npm view eslint-plugin-myplugin peerDepencies` to see what peer dependencies `eslint-plugin-myplugin` has.
|
225
|
+
|
214
226
|
### Does ESLint support JSX?
|
215
227
|
|
216
228
|
Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/user-guide/configuring).). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.
|
package/lib/cli-engine.js
CHANGED
@@ -240,8 +240,8 @@ function processFile(filename, configHelper, options, linter) {
|
|
240
240
|
function createIgnoreResult(filePath, baseDir) {
|
241
241
|
let message;
|
242
242
|
const isHidden = /^\./.test(path.basename(filePath));
|
243
|
-
const isInNodeModules = baseDir &&
|
244
|
-
const isInBowerComponents = baseDir &&
|
243
|
+
const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
|
244
|
+
const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
|
245
245
|
|
246
246
|
if (isHidden) {
|
247
247
|
message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
|
package/lib/cli.js
CHANGED
@@ -144,6 +144,8 @@ const cli = {
|
|
144
144
|
|
145
145
|
const files = currentOptions._;
|
146
146
|
|
147
|
+
const useStdin = typeof text === "string";
|
148
|
+
|
147
149
|
if (currentOptions.version) { // version from package.json
|
148
150
|
|
149
151
|
log.info(`v${require("../package.json").version}`);
|
@@ -153,7 +155,7 @@ const cli = {
|
|
153
155
|
log.error("The --print-config option must be used with exactly one file name.");
|
154
156
|
return 1;
|
155
157
|
}
|
156
|
-
if (
|
158
|
+
if (useStdin) {
|
157
159
|
log.error("The --print-config option is not available for piped-in code.");
|
158
160
|
return 1;
|
159
161
|
}
|
@@ -164,27 +166,27 @@ const cli = {
|
|
164
166
|
|
165
167
|
log.info(JSON.stringify(fileConfig, null, " "));
|
166
168
|
return 0;
|
167
|
-
} else if (currentOptions.help || (!files.length && !
|
169
|
+
} else if (currentOptions.help || (!files.length && !useStdin)) {
|
168
170
|
|
169
171
|
log.info(options.generateHelp());
|
170
172
|
|
171
173
|
} else {
|
172
174
|
|
173
|
-
debug(`Running on ${
|
175
|
+
debug(`Running on ${useStdin ? "text" : "files"}`);
|
174
176
|
|
175
177
|
if (currentOptions.fix && currentOptions.fixDryRun) {
|
176
178
|
log.error("The --fix option and the --fix-dry-run option cannot be used together.");
|
177
179
|
return 1;
|
178
180
|
}
|
179
181
|
|
180
|
-
if (
|
182
|
+
if (useStdin && currentOptions.fix) {
|
181
183
|
log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead.");
|
182
184
|
return 1;
|
183
185
|
}
|
184
186
|
|
185
187
|
const engine = new CLIEngine(translateOptions(currentOptions));
|
186
188
|
|
187
|
-
const report =
|
189
|
+
const report = useStdin ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files);
|
188
190
|
|
189
191
|
if (currentOptions.fix) {
|
190
192
|
debug("Fix mode enabled - applying fixes");
|
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
|
|
@@ -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
|
/**
|
package/lib/rules/indent.js
CHANGED
@@ -1245,7 +1245,9 @@ module.exports = {
|
|
1245
1245
|
NewExpression(node) {
|
1246
1246
|
|
1247
1247
|
// Only indent the arguments if the NewExpression has parens (e.g. `new Foo(bar)` or `new Foo()`, but not `new Foo`
|
1248
|
-
if (node.arguments.length > 0 ||
|
1248
|
+
if (node.arguments.length > 0 ||
|
1249
|
+
astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
|
1250
|
+
astUtils.isOpeningParenToken(sourceCode.getLastToken(node, 1))) {
|
1249
1251
|
addFunctionCallIndent(node);
|
1250
1252
|
}
|
1251
1253
|
},
|
@@ -308,7 +308,10 @@ module.exports = {
|
|
308
308
|
nextLineNum = token.loc.end.line + 1,
|
309
309
|
commentIsNotAlone = codeAroundComment(token);
|
310
310
|
|
311
|
-
const blockStartAllowed = options.allowBlockStart &&
|
311
|
+
const blockStartAllowed = options.allowBlockStart &&
|
312
|
+
isCommentAtBlockStart(token) &&
|
313
|
+
!(options.allowClassStart === false &&
|
314
|
+
isCommentAtClassStart(token)),
|
312
315
|
blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
|
313
316
|
classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
|
314
317
|
classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
|
@@ -125,10 +125,12 @@ module.exports = {
|
|
125
125
|
},
|
126
126
|
message: EXPECTED_BLOCK_ERROR,
|
127
127
|
fix(fixer) {
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
)
|
128
|
+
const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
|
129
|
+
const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
|
130
|
+
|
131
|
+
return commentLines.some(value => value.startsWith("/"))
|
132
|
+
? null
|
133
|
+
: fixer.replaceTextRange(range, starredBlock);
|
132
134
|
}
|
133
135
|
});
|
134
136
|
} else {
|
@@ -174,8 +176,12 @@ module.exports = {
|
|
174
176
|
: MISSING_STAR_ERROR,
|
175
177
|
fix(fixer) {
|
176
178
|
const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 });
|
177
|
-
const
|
178
|
-
const
|
179
|
+
const linePrefixLength = lineText.match(/^\s*\*? ?/)[0].length;
|
180
|
+
const commentStartIndex = lineStartIndex + linePrefixLength;
|
181
|
+
|
182
|
+
const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength
|
183
|
+
? expectedLinePrefix
|
184
|
+
: `${expectedLinePrefix} `;
|
179
185
|
|
180
186
|
return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText);
|
181
187
|
}
|
@@ -188,6 +194,11 @@ module.exports = {
|
|
188
194
|
if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") {
|
189
195
|
const commentLines = getCommentLines(commentGroup);
|
190
196
|
const block = commentGroup[0];
|
197
|
+
const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true });
|
198
|
+
|
199
|
+
if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) {
|
200
|
+
return;
|
201
|
+
}
|
191
202
|
|
192
203
|
context.report({
|
193
204
|
loc: {
|
@@ -260,10 +271,13 @@ module.exports = {
|
|
260
271
|
return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
|
261
272
|
})
|
262
273
|
.reduce((commentGroups, comment, index, commentList) => {
|
274
|
+
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
275
|
+
|
263
276
|
if (
|
264
277
|
comment.type === "Line" &&
|
265
278
|
index && commentList[index - 1].type === "Line" &&
|
266
|
-
|
279
|
+
tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
|
280
|
+
tokenBefore === commentList[index - 1]
|
267
281
|
) {
|
268
282
|
commentGroups[commentGroups.length - 1].push(comment);
|
269
283
|
} else {
|
@@ -51,7 +51,7 @@ module.exports = {
|
|
51
51
|
CatchClause(node) {
|
52
52
|
let scope = context.getScope();
|
53
53
|
|
54
|
-
// When
|
54
|
+
// When ecmaVersion >= 6, CatchClause creates its own scope
|
55
55
|
// so start from one upper scope to exclude the current node
|
56
56
|
if (scope.block === node) {
|
57
57
|
scope = scope.upper;
|
@@ -138,7 +138,7 @@ module.exports = {
|
|
138
138
|
function checkConstantConditionLoopInSet(node) {
|
139
139
|
if (loopsInCurrentScope.has(node)) {
|
140
140
|
loopsInCurrentScope.delete(node);
|
141
|
-
context.report({ node, message: "Unexpected constant condition." });
|
141
|
+
context.report({ node: node.test, message: "Unexpected constant condition." });
|
142
142
|
}
|
143
143
|
}
|
144
144
|
|
@@ -150,7 +150,7 @@ module.exports = {
|
|
150
150
|
*/
|
151
151
|
function reportIfConstant(node) {
|
152
152
|
if (node.test && isConstant(node.test, true)) {
|
153
|
-
context.report({ node, message: "Unexpected constant condition." });
|
153
|
+
context.report({ node: node.test, message: "Unexpected constant condition." });
|
154
154
|
}
|
155
155
|
}
|
156
156
|
|
@@ -45,7 +45,8 @@ module.exports = {
|
|
45
45
|
const lastIfToken = sourceCode.getLastToken(node.consequent);
|
46
46
|
const sourceText = sourceCode.getText();
|
47
47
|
|
48
|
-
if (sourceText.slice(openingElseCurly.range[1],
|
48
|
+
if (sourceText.slice(openingElseCurly.range[1],
|
49
|
+
node.range[0]).trim() || sourceText.slice(node.range[1], closingElseCurly.range[0]).trim()) {
|
49
50
|
|
50
51
|
// Don't fix if there are any non-whitespace characters interfering (e.g. comments)
|
51
52
|
return null;
|
@@ -49,7 +49,7 @@ module.exports = {
|
|
49
49
|
|
50
50
|
const options = context.options[0] || {},
|
51
51
|
skipBlankLines = options.skipBlankLines || false,
|
52
|
-
ignoreComments = typeof options.ignoreComments === "
|
52
|
+
ignoreComments = typeof options.ignoreComments === "boolean" && options.ignoreComments;
|
53
53
|
|
54
54
|
/**
|
55
55
|
* Report the error message
|
@@ -72,8 +72,10 @@ module.exports = {
|
|
72
72
|
node.right,
|
73
73
|
token => token.value === node.operator
|
74
74
|
);
|
75
|
+
const text = sourceCode.getText();
|
75
76
|
|
76
|
-
return
|
77
|
+
return text.slice(node.range[0],
|
78
|
+
operatorToken.range[0]) + OPERATOR_INVERSES[node.operator] + text.slice(operatorToken.range[1], node.range[1]);
|
77
79
|
}
|
78
80
|
|
79
81
|
if (astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression" })) {
|
@@ -59,7 +59,8 @@ module.exports = {
|
|
59
59
|
* Only perform a fix if there are no comments between the label and the body. This will be the case
|
60
60
|
* when there is exactly one token/comment (the ":") between the label and the body.
|
61
61
|
*/
|
62
|
-
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
|
62
|
+
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
|
63
|
+
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
|
63
64
|
return fixer.removeRange([node.range[0], node.body.range[0]]);
|
64
65
|
}
|
65
66
|
|
@@ -50,7 +50,8 @@ module.exports = {
|
|
50
50
|
const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken);
|
51
51
|
const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1);
|
52
52
|
|
53
|
-
if (tokensBetween.slice(0, -1).some((token, index) =>
|
53
|
+
if (tokensBetween.slice(0, -1).some((token, index) =>
|
54
|
+
sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) {
|
54
55
|
|
55
56
|
// If there are comments between the brackets and the property name, don't do a fix.
|
56
57
|
return null;
|
@@ -63,7 +63,14 @@ function parseRegExp(regExpText) {
|
|
63
63
|
return Object.assign(state, { inCharClass: false, startingCharClass: false });
|
64
64
|
}
|
65
65
|
}
|
66
|
-
charList.push({
|
66
|
+
charList.push({
|
67
|
+
text: char,
|
68
|
+
index,
|
69
|
+
escaped: state.escapeNextChar,
|
70
|
+
inCharClass: state.inCharClass,
|
71
|
+
startsCharClass: state.startingCharClass,
|
72
|
+
endsCharClass: false
|
73
|
+
});
|
67
74
|
return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
|
68
75
|
}, { escapeNextChar: false, inCharClass: false, startingCharClass: false });
|
69
76
|
|
package/lib/rules/no-var.js
CHANGED
@@ -15,6 +15,15 @@ const astUtils = require("../ast-utils");
|
|
15
15
|
// Helpers
|
16
16
|
//------------------------------------------------------------------------------
|
17
17
|
|
18
|
+
/**
|
19
|
+
* Check whether a given variable is a global variable or not.
|
20
|
+
* @param {eslint-scope.Variable} variable The variable to check.
|
21
|
+
* @returns {boolean} `true` if the variable is a global variable.
|
22
|
+
*/
|
23
|
+
function isGlobal(variable) {
|
24
|
+
return Boolean(variable.scope) && variable.scope.type === "global";
|
25
|
+
}
|
26
|
+
|
18
27
|
/**
|
19
28
|
* Finds the nearest function scope or global scope walking up the scope
|
20
29
|
* hierarchy.
|
@@ -203,6 +212,7 @@ module.exports = {
|
|
203
212
|
* Checks whether it can fix a given variable declaration or not.
|
204
213
|
* It cannot fix if the following cases:
|
205
214
|
*
|
215
|
+
* - A variable is a global variable.
|
206
216
|
* - A variable is declared on a SwitchCase node.
|
207
217
|
* - A variable is redeclared.
|
208
218
|
* - A variable is used from outside the scope.
|
@@ -256,6 +266,7 @@ module.exports = {
|
|
256
266
|
|
257
267
|
if (node.parent.type === "SwitchCase" ||
|
258
268
|
node.declarations.some(hasSelfReferenceInTDZ) ||
|
269
|
+
variables.some(isGlobal) ||
|
259
270
|
variables.some(isRedeclared) ||
|
260
271
|
variables.some(isUsedFromOutsideOf(scopeNode))
|
261
272
|
) {
|
@@ -215,8 +215,12 @@ module.exports = {
|
|
215
215
|
* @returns {Object} A fix for this node
|
216
216
|
*/
|
217
217
|
function makeFunctionShorthand(fixer, node) {
|
218
|
-
const firstKeyToken = node.computed
|
219
|
-
|
218
|
+
const firstKeyToken = node.computed
|
219
|
+
? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken)
|
220
|
+
: sourceCode.getFirstToken(node.key);
|
221
|
+
const lastKeyToken = node.computed
|
222
|
+
? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken)
|
223
|
+
: sourceCode.getLastToken(node.key);
|
220
224
|
const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
|
221
225
|
let keyPrefix = "";
|
222
226
|
|
@@ -87,7 +87,9 @@ module.exports = {
|
|
87
87
|
if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
|
88
88
|
|
89
89
|
// If there is a comment before and after the operator, don't do a fix.
|
90
|
-
if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore &&
|
90
|
+
if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore &&
|
91
|
+
sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
|
92
|
+
|
91
93
|
return null;
|
92
94
|
}
|
93
95
|
|
@@ -151,7 +151,8 @@ module.exports = {
|
|
151
151
|
message: "Member '{{memberName}}' of the import declaration should be sorted alphabetically.",
|
152
152
|
data: { memberName: importSpecifiers[firstUnsortedIndex].local.name },
|
153
153
|
fix(fixer) {
|
154
|
-
if (importSpecifiers.some(specifier =>
|
154
|
+
if (importSpecifiers.some(specifier =>
|
155
|
+
sourceCode.getCommentsBefore(specifier).length || sourceCode.getCommentsAfter(specifier).length)) {
|
155
156
|
|
156
157
|
// If there are comments in the ImportSpecifier list, don't rearrange the specifiers.
|
157
158
|
return null;
|
package/lib/rules/valid-jsdoc.js
CHANGED
@@ -226,8 +226,10 @@ module.exports = {
|
|
226
226
|
function checkJSDoc(node) {
|
227
227
|
const jsdocNode = sourceCode.getJSDocComment(node),
|
228
228
|
functionData = fns.pop(),
|
229
|
-
params = Object.create(null)
|
229
|
+
params = Object.create(null),
|
230
|
+
paramsTags = [];
|
230
231
|
let hasReturns = false,
|
232
|
+
returnsTag,
|
231
233
|
hasConstructor = false,
|
232
234
|
isInterface = false,
|
233
235
|
isOverride = false,
|
@@ -261,43 +263,13 @@ module.exports = {
|
|
261
263
|
case "param":
|
262
264
|
case "arg":
|
263
265
|
case "argument":
|
264
|
-
|
265
|
-
context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: tag.name } });
|
266
|
-
}
|
267
|
-
|
268
|
-
if (!tag.description && requireParamDescription) {
|
269
|
-
context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: tag.name } });
|
270
|
-
}
|
271
|
-
|
272
|
-
if (params[tag.name]) {
|
273
|
-
context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: tag.name } });
|
274
|
-
} else if (tag.name.indexOf(".") === -1) {
|
275
|
-
params[tag.name] = 1;
|
276
|
-
}
|
266
|
+
paramsTags.push(tag);
|
277
267
|
break;
|
278
268
|
|
279
269
|
case "return":
|
280
270
|
case "returns":
|
281
271
|
hasReturns = true;
|
282
|
-
|
283
|
-
if (!requireReturn && !functionData.returnPresent && (tag.type === null || !isValidReturnType(tag)) && !isAbstract) {
|
284
|
-
context.report({
|
285
|
-
node: jsdocNode,
|
286
|
-
message: "Unexpected @{{title}} tag; function has no return statement.",
|
287
|
-
data: {
|
288
|
-
title: tag.title
|
289
|
-
}
|
290
|
-
});
|
291
|
-
} else {
|
292
|
-
if (requireReturnType && !tag.type) {
|
293
|
-
context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
|
294
|
-
}
|
295
|
-
|
296
|
-
if (!isValidReturnType(tag) && !tag.description && requireReturnDescription) {
|
297
|
-
context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
|
298
|
-
}
|
299
|
-
}
|
300
|
-
|
272
|
+
returnsTag = tag;
|
301
273
|
break;
|
302
274
|
|
303
275
|
case "constructor":
|
@@ -333,6 +305,40 @@ module.exports = {
|
|
333
305
|
}
|
334
306
|
});
|
335
307
|
|
308
|
+
paramsTags.forEach(param => {
|
309
|
+
if (!param.type) {
|
310
|
+
context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: param.name } });
|
311
|
+
}
|
312
|
+
if (!param.description && requireParamDescription) {
|
313
|
+
context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: param.name } });
|
314
|
+
}
|
315
|
+
if (params[param.name]) {
|
316
|
+
context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: param.name } });
|
317
|
+
} else if (param.name.indexOf(".") === -1) {
|
318
|
+
params[param.name] = 1;
|
319
|
+
}
|
320
|
+
});
|
321
|
+
|
322
|
+
if (hasReturns) {
|
323
|
+
if (!requireReturn && !functionData.returnPresent && (returnsTag.type === null || !isValidReturnType(returnsTag)) && !isAbstract) {
|
324
|
+
context.report({
|
325
|
+
node: jsdocNode,
|
326
|
+
message: "Unexpected @{{title}} tag; function has no return statement.",
|
327
|
+
data: {
|
328
|
+
title: returnsTag.title
|
329
|
+
}
|
330
|
+
});
|
331
|
+
} else {
|
332
|
+
if (requireReturnType && !returnsTag.type) {
|
333
|
+
context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
|
334
|
+
}
|
335
|
+
|
336
|
+
if (!isValidReturnType(returnsTag) && !returnsTag.description && requireReturnDescription) {
|
337
|
+
context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
|
338
|
+
}
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
336
342
|
// check for functions missing @returns
|
337
343
|
if (!isOverride && !hasReturns && !hasConstructor && !isInterface &&
|
338
344
|
node.parent.kind !== "get" && node.parent.kind !== "constructor" &&
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "4.
|
3
|
+
"version": "4.10.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -20,8 +20,7 @@
|
|
20
20
|
"browserify": "node Makefile.js browserify",
|
21
21
|
"perf": "node Makefile.js perf",
|
22
22
|
"profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree",
|
23
|
-
"coveralls": "cat ./coverage/lcov.info | coveralls"
|
24
|
-
"check-commit": "node Makefile.js checkGitCommit"
|
23
|
+
"coveralls": "cat ./coverage/lcov.info | coveralls"
|
25
24
|
},
|
26
25
|
"files": [
|
27
26
|
"LICENSE",
|
@@ -1,130 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview Internal rule to enforce meta.docs.description conventions.
|
3
|
-
* @author Vitor Balocco
|
4
|
-
*/
|
5
|
-
|
6
|
-
"use strict";
|
7
|
-
|
8
|
-
const ALLOWED_FIRST_WORDS = [
|
9
|
-
"enforce",
|
10
|
-
"require",
|
11
|
-
"disallow"
|
12
|
-
];
|
13
|
-
|
14
|
-
//------------------------------------------------------------------------------
|
15
|
-
// Helpers
|
16
|
-
//------------------------------------------------------------------------------
|
17
|
-
|
18
|
-
/**
|
19
|
-
* Gets the property of the Object node passed in that has the name specified.
|
20
|
-
*
|
21
|
-
* @param {string} property Name of the property to return.
|
22
|
-
* @param {ASTNode} node The ObjectExpression node.
|
23
|
-
* @returns {ASTNode} The Property node or null if not found.
|
24
|
-
*/
|
25
|
-
function getPropertyFromObject(property, node) {
|
26
|
-
const properties = node.properties;
|
27
|
-
|
28
|
-
for (let i = 0; i < properties.length; i++) {
|
29
|
-
if (properties[i].key.name === property) {
|
30
|
-
return properties[i];
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
return null;
|
35
|
-
}
|
36
|
-
|
37
|
-
/**
|
38
|
-
* Verifies that the meta.docs.description property follows our internal conventions.
|
39
|
-
*
|
40
|
-
* @param {RuleContext} context The ESLint rule context.
|
41
|
-
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
|
42
|
-
* @returns {void}
|
43
|
-
*/
|
44
|
-
function checkMetaDocsDescription(context, exportsNode) {
|
45
|
-
if (exportsNode.type !== "ObjectExpression") {
|
46
|
-
|
47
|
-
// if the exported node is not the correct format, "internal-no-invalid-meta" will already report this.
|
48
|
-
return;
|
49
|
-
}
|
50
|
-
|
51
|
-
const metaProperty = getPropertyFromObject("meta", exportsNode);
|
52
|
-
const metaDocs = metaProperty && getPropertyFromObject("docs", metaProperty.value);
|
53
|
-
const metaDocsDescription = metaDocs && getPropertyFromObject("description", metaDocs.value);
|
54
|
-
|
55
|
-
if (!metaDocsDescription) {
|
56
|
-
|
57
|
-
// if there is no `meta.docs.description` property, "internal-no-invalid-meta" will already report this.
|
58
|
-
return;
|
59
|
-
}
|
60
|
-
|
61
|
-
const description = metaDocsDescription.value.value;
|
62
|
-
|
63
|
-
if (typeof description !== "string") {
|
64
|
-
context.report({
|
65
|
-
node: metaDocsDescription.value,
|
66
|
-
message: "`meta.docs.description` should be a string."
|
67
|
-
});
|
68
|
-
return;
|
69
|
-
}
|
70
|
-
|
71
|
-
if (description === "") {
|
72
|
-
context.report({
|
73
|
-
node: metaDocsDescription.value,
|
74
|
-
message: "`meta.docs.description` should not be empty."
|
75
|
-
});
|
76
|
-
return;
|
77
|
-
}
|
78
|
-
|
79
|
-
if (description.indexOf(" ") === 0) {
|
80
|
-
context.report({
|
81
|
-
node: metaDocsDescription.value,
|
82
|
-
message: "`meta.docs.description` should not start with whitespace."
|
83
|
-
});
|
84
|
-
return;
|
85
|
-
}
|
86
|
-
|
87
|
-
const firstWord = description.split(" ")[0];
|
88
|
-
|
89
|
-
if (ALLOWED_FIRST_WORDS.indexOf(firstWord) === -1) {
|
90
|
-
context.report({
|
91
|
-
node: metaDocsDescription.value,
|
92
|
-
message: "`meta.docs.description` should start with one of the following words: {{ allowedWords }}. Started with \"{{ firstWord }}\" instead.",
|
93
|
-
data: {
|
94
|
-
allowedWords: ALLOWED_FIRST_WORDS.join(", "),
|
95
|
-
firstWord
|
96
|
-
}
|
97
|
-
});
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
//------------------------------------------------------------------------------
|
102
|
-
// Rule Definition
|
103
|
-
//------------------------------------------------------------------------------
|
104
|
-
|
105
|
-
module.exports = {
|
106
|
-
meta: {
|
107
|
-
docs: {
|
108
|
-
description: "enforce correct conventions of `meta.docs.description` property in core rules",
|
109
|
-
category: "Internal",
|
110
|
-
recommended: false
|
111
|
-
},
|
112
|
-
|
113
|
-
schema: []
|
114
|
-
},
|
115
|
-
|
116
|
-
create(context) {
|
117
|
-
return {
|
118
|
-
AssignmentExpression(node) {
|
119
|
-
if (node.left &&
|
120
|
-
node.right &&
|
121
|
-
node.left.type === "MemberExpression" &&
|
122
|
-
node.left.object.name === "module" &&
|
123
|
-
node.left.property.name === "exports") {
|
124
|
-
|
125
|
-
checkMetaDocsDescription(context, node.right);
|
126
|
-
}
|
127
|
-
}
|
128
|
-
};
|
129
|
-
}
|
130
|
-
};
|
@@ -1,188 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview Internal rule to prevent missing or invalid meta property in core rules.
|
3
|
-
* @author Vitor Balocco
|
4
|
-
*/
|
5
|
-
|
6
|
-
"use strict";
|
7
|
-
|
8
|
-
//------------------------------------------------------------------------------
|
9
|
-
// Helpers
|
10
|
-
//------------------------------------------------------------------------------
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Gets the property of the Object node passed in that has the name specified.
|
14
|
-
*
|
15
|
-
* @param {string} property Name of the property to return.
|
16
|
-
* @param {ASTNode} node The ObjectExpression node.
|
17
|
-
* @returns {ASTNode} The Property node or null if not found.
|
18
|
-
*/
|
19
|
-
function getPropertyFromObject(property, node) {
|
20
|
-
const properties = node.properties;
|
21
|
-
|
22
|
-
for (let i = 0; i < properties.length; i++) {
|
23
|
-
if (properties[i].key.name === property) {
|
24
|
-
return properties[i];
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
return null;
|
29
|
-
}
|
30
|
-
|
31
|
-
/**
|
32
|
-
* Extracts the `meta` property from the ObjectExpression that all rules export.
|
33
|
-
*
|
34
|
-
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
|
35
|
-
* @returns {ASTNode} The `meta` Property node or null if not found.
|
36
|
-
*/
|
37
|
-
function getMetaPropertyFromExportsNode(exportsNode) {
|
38
|
-
return getPropertyFromObject("meta", exportsNode);
|
39
|
-
}
|
40
|
-
|
41
|
-
/**
|
42
|
-
* Whether this `meta` ObjectExpression has a `docs` property defined or not.
|
43
|
-
*
|
44
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
45
|
-
* @returns {boolean} `true` if a `docs` property exists.
|
46
|
-
*/
|
47
|
-
function hasMetaDocs(metaPropertyNode) {
|
48
|
-
return Boolean(getPropertyFromObject("docs", metaPropertyNode.value));
|
49
|
-
}
|
50
|
-
|
51
|
-
/**
|
52
|
-
* Whether this `meta` ObjectExpression has a `docs.description` property defined or not.
|
53
|
-
*
|
54
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
55
|
-
* @returns {boolean} `true` if a `docs.description` property exists.
|
56
|
-
*/
|
57
|
-
function hasMetaDocsDescription(metaPropertyNode) {
|
58
|
-
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
|
59
|
-
|
60
|
-
return metaDocs && getPropertyFromObject("description", metaDocs.value);
|
61
|
-
}
|
62
|
-
|
63
|
-
/**
|
64
|
-
* Whether this `meta` ObjectExpression has a `docs.category` property defined or not.
|
65
|
-
*
|
66
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
67
|
-
* @returns {boolean} `true` if a `docs.category` property exists.
|
68
|
-
*/
|
69
|
-
function hasMetaDocsCategory(metaPropertyNode) {
|
70
|
-
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
|
71
|
-
|
72
|
-
return metaDocs && getPropertyFromObject("category", metaDocs.value);
|
73
|
-
}
|
74
|
-
|
75
|
-
/**
|
76
|
-
* Whether this `meta` ObjectExpression has a `docs.recommended` property defined or not.
|
77
|
-
*
|
78
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
79
|
-
* @returns {boolean} `true` if a `docs.recommended` property exists.
|
80
|
-
*/
|
81
|
-
function hasMetaDocsRecommended(metaPropertyNode) {
|
82
|
-
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
|
83
|
-
|
84
|
-
return metaDocs && getPropertyFromObject("recommended", metaDocs.value);
|
85
|
-
}
|
86
|
-
|
87
|
-
/**
|
88
|
-
* Whether this `meta` ObjectExpression has a `schema` property defined or not.
|
89
|
-
*
|
90
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
91
|
-
* @returns {boolean} `true` if a `schema` property exists.
|
92
|
-
*/
|
93
|
-
function hasMetaSchema(metaPropertyNode) {
|
94
|
-
return getPropertyFromObject("schema", metaPropertyNode.value);
|
95
|
-
}
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Checks the validity of the meta definition of this rule and reports any errors found.
|
99
|
-
*
|
100
|
-
* @param {RuleContext} context The ESLint rule context.
|
101
|
-
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
|
102
|
-
* @param {boolean} ruleIsFixable whether the rule is fixable or not.
|
103
|
-
* @returns {void}
|
104
|
-
*/
|
105
|
-
function checkMetaValidity(context, exportsNode) {
|
106
|
-
const metaProperty = getMetaPropertyFromExportsNode(exportsNode);
|
107
|
-
|
108
|
-
if (!metaProperty) {
|
109
|
-
context.report(exportsNode, "Rule is missing a meta property.");
|
110
|
-
return;
|
111
|
-
}
|
112
|
-
|
113
|
-
if (!hasMetaDocs(metaProperty)) {
|
114
|
-
context.report(metaProperty, "Rule is missing a meta.docs property.");
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
|
118
|
-
if (!hasMetaDocsDescription(metaProperty)) {
|
119
|
-
context.report(metaProperty, "Rule is missing a meta.docs.description property.");
|
120
|
-
return;
|
121
|
-
}
|
122
|
-
|
123
|
-
if (!hasMetaDocsCategory(metaProperty)) {
|
124
|
-
context.report(metaProperty, "Rule is missing a meta.docs.category property.");
|
125
|
-
return;
|
126
|
-
}
|
127
|
-
|
128
|
-
if (!hasMetaDocsRecommended(metaProperty)) {
|
129
|
-
context.report(metaProperty, "Rule is missing a meta.docs.recommended property.");
|
130
|
-
return;
|
131
|
-
}
|
132
|
-
|
133
|
-
if (!hasMetaSchema(metaProperty)) {
|
134
|
-
context.report(metaProperty, "Rule is missing a meta.schema property.");
|
135
|
-
}
|
136
|
-
}
|
137
|
-
|
138
|
-
/**
|
139
|
-
* Whether this node is the correct format for a rule definition or not.
|
140
|
-
*
|
141
|
-
* @param {ASTNode} node node that the rule exports.
|
142
|
-
* @returns {boolean} `true` if the exported node is the correct format for a rule definition
|
143
|
-
*/
|
144
|
-
function isCorrectExportsFormat(node) {
|
145
|
-
return node.type === "ObjectExpression";
|
146
|
-
}
|
147
|
-
|
148
|
-
//------------------------------------------------------------------------------
|
149
|
-
// Rule Definition
|
150
|
-
//------------------------------------------------------------------------------
|
151
|
-
|
152
|
-
module.exports = {
|
153
|
-
meta: {
|
154
|
-
docs: {
|
155
|
-
description: "enforce correct use of `meta` property in core rules",
|
156
|
-
category: "Internal",
|
157
|
-
recommended: false
|
158
|
-
},
|
159
|
-
|
160
|
-
schema: []
|
161
|
-
},
|
162
|
-
|
163
|
-
create(context) {
|
164
|
-
let exportsNode;
|
165
|
-
|
166
|
-
return {
|
167
|
-
AssignmentExpression(node) {
|
168
|
-
if (node.left &&
|
169
|
-
node.right &&
|
170
|
-
node.left.type === "MemberExpression" &&
|
171
|
-
node.left.object.name === "module" &&
|
172
|
-
node.left.property.name === "exports") {
|
173
|
-
|
174
|
-
exportsNode = node.right;
|
175
|
-
}
|
176
|
-
},
|
177
|
-
|
178
|
-
"Program:exit"() {
|
179
|
-
if (!isCorrectExportsFormat(exportsNode)) {
|
180
|
-
context.report({ node: exportsNode, message: "Rule does not export an Object. Make sure the rule follows the new rule format." });
|
181
|
-
return;
|
182
|
-
}
|
183
|
-
|
184
|
-
checkMetaValidity(context, exportsNode);
|
185
|
-
}
|
186
|
-
};
|
187
|
-
}
|
188
|
-
};
|