]>
Commit | Line | Data |
---|---|---|
56c4a2cb DC |
1 | /** |
2 | * @fileoverview Tests for the ESLint class. | |
3 | * @author Kai Cataldo | |
4 | * @author Toru Nagashima | |
5 | */ | |
6 | ||
7 | "use strict"; | |
8 | ||
9 | //------------------------------------------------------------------------------ | |
10 | // Requirements | |
11 | //------------------------------------------------------------------------------ | |
12 | ||
13 | const assert = require("assert"); | |
14 | const fs = require("fs"); | |
15 | const os = require("os"); | |
16 | const path = require("path"); | |
17 | const escapeStringRegExp = require("escape-string-regexp"); | |
18 | const fCache = require("file-entry-cache"); | |
56c4a2cb DC |
19 | const sinon = require("sinon"); |
20 | const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); | |
21 | const shell = require("shelljs"); | |
609c276f TL |
22 | const { |
23 | Legacy: { | |
24 | CascadingConfigArrayFactory | |
25 | } | |
26 | } = require("@eslint/eslintrc"); | |
56c4a2cb | 27 | const hash = require("../../../lib/cli-engine/hash"); |
6f036462 | 28 | const { unIndent, createCustomTeardown } = require("../../_utils"); |
609c276f | 29 | const coreRules = require("../../../lib/rules"); |
8f9d1d4d | 30 | const childProcess = require("child_process"); |
56c4a2cb DC |
31 | |
32 | //------------------------------------------------------------------------------ | |
33 | // Tests | |
34 | //------------------------------------------------------------------------------ | |
35 | ||
36 | describe("ESLint", () => { | |
37 | const examplePluginName = "eslint-plugin-example"; | |
38 | const examplePluginNameWithNamespace = "@eslint/eslint-plugin-example"; | |
39 | const examplePlugin = { | |
40 | rules: { | |
41 | "example-rule": require("../../fixtures/rules/custom-rule"), | |
42 | "make-syntax-error": require("../../fixtures/rules/make-syntax-error-rule") | |
43 | } | |
44 | }; | |
45 | const examplePreprocessorName = "eslint-plugin-processor"; | |
46 | const originalDir = process.cwd(); | |
47 | const fixtureDir = path.resolve(fs.realpathSync(os.tmpdir()), "eslint/fixtures"); | |
48 | ||
609c276f | 49 | /** @type {import("../../../lib/eslint").ESLint} */ |
56c4a2cb DC |
50 | let ESLint; |
51 | ||
52 | /** | |
53 | * Returns the path inside of the fixture directory. | |
54 | * @param {...string} args file path segments. | |
55 | * @returns {string} The path inside the fixture directory. | |
56 | * @private | |
57 | */ | |
58 | function getFixturePath(...args) { | |
59 | const filepath = path.join(fixtureDir, ...args); | |
60 | ||
61 | try { | |
62 | return fs.realpathSync(filepath); | |
d3726936 | 63 | } catch { |
56c4a2cb DC |
64 | return filepath; |
65 | } | |
66 | } | |
67 | ||
68 | /** | |
69 | * Create the ESLint object by mocking some of the plugins | |
70 | * @param {Object} options options for ESLint | |
71 | * @returns {ESLint} engine object | |
72 | * @private | |
73 | */ | |
74 | function eslintWithPlugins(options) { | |
75 | return new ESLint({ | |
76 | ...options, | |
77 | plugins: { | |
78 | [examplePluginName]: examplePlugin, | |
79 | [examplePluginNameWithNamespace]: examplePlugin, | |
80 | [examplePreprocessorName]: require("../../fixtures/processors/custom-processor") | |
81 | } | |
82 | }); | |
83 | } | |
84 | ||
85 | /** | |
86 | * Call the last argument. | |
87 | * @param {any[]} args Arguments | |
88 | * @returns {void} | |
89 | */ | |
90 | function callLastArgument(...args) { | |
91 | process.nextTick(args[args.length - 1], null); | |
92 | } | |
93 | ||
94 | // copy into clean area so as not to get "infected" by this project's .eslintrc files | |
6f036462 TL |
95 | before(function() { |
96 | ||
97 | /* | |
98 | * GitHub Actions Windows and macOS runners occasionally exhibit | |
99 | * extremely slow filesystem operations, during which copying fixtures | |
100 | * exceeds the default test timeout, so raise it just for this hook. | |
101 | * Mocha uses `this` to set timeouts on an individual hook level. | |
102 | */ | |
609c276f | 103 | this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API |
56c4a2cb DC |
104 | shell.mkdir("-p", fixtureDir); |
105 | shell.cp("-r", "./tests/fixtures/.", fixtureDir); | |
106 | }); | |
107 | ||
108 | beforeEach(() => { | |
109 | ({ ESLint } = require("../../../lib/eslint/eslint")); | |
110 | }); | |
111 | ||
112 | after(() => { | |
113 | shell.rm("-r", fixtureDir); | |
114 | }); | |
115 | ||
116 | describe("ESLint constructor function", () => { | |
117 | it("the default value of 'options.cwd' should be the current working directory.", async () => { | |
118 | process.chdir(__dirname); | |
119 | try { | |
120 | const engine = new ESLint(); | |
121 | const results = await engine.lintFiles("eslint.js"); | |
122 | ||
123 | assert.strictEqual(path.dirname(results[0].filePath), __dirname); | |
124 | } finally { | |
125 | process.chdir(originalDir); | |
126 | } | |
127 | }); | |
128 | ||
129 | it("should report one fatal message when given a path by --ignore-path that is not a file when ignore is true.", () => { | |
130 | assert.throws(() => { | |
609c276f | 131 | // eslint-disable-next-line no-new -- Check for throwing |
56c4a2cb DC |
132 | new ESLint({ ignorePath: fixtureDir }); |
133 | }, new RegExp(escapeStringRegExp(`Cannot read .eslintignore file: ${fixtureDir}\nError: EISDIR: illegal operation on a directory, read`), "u")); | |
134 | }); | |
135 | ||
136 | // https://github.com/eslint/eslint/issues/2380 | |
137 | it("should not modify baseConfig when format is specified", () => { | |
138 | const customBaseConfig = { root: true }; | |
139 | ||
609c276f | 140 | new ESLint({ baseConfig: customBaseConfig }); // eslint-disable-line no-new -- Check for argument side effects |
56c4a2cb DC |
141 | |
142 | assert.deepStrictEqual(customBaseConfig, { root: true }); | |
143 | }); | |
144 | ||
145 | it("should throw readable messages if removed options are present", () => { | |
146 | assert.throws( | |
147 | () => new ESLint({ | |
148 | cacheFile: "", | |
149 | configFile: "", | |
150 | envs: [], | |
151 | globals: [], | |
152 | ignorePattern: [], | |
153 | parser: "", | |
154 | parserOptions: {}, | |
155 | rules: {}, | |
156 | plugins: [] | |
157 | }), | |
158 | new RegExp(escapeStringRegExp([ | |
159 | "Invalid Options:", | |
160 | "- Unknown options: cacheFile, configFile, envs, globals, ignorePattern, parser, parserOptions, rules", | |
161 | "- 'cacheFile' has been removed. Please use the 'cacheLocation' option instead.", | |
162 | "- 'configFile' has been removed. Please use the 'overrideConfigFile' option instead.", | |
163 | "- 'envs' has been removed. Please use the 'overrideConfig.env' option instead.", | |
164 | "- 'globals' has been removed. Please use the 'overrideConfig.globals' option instead.", | |
165 | "- 'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.", | |
166 | "- 'parser' has been removed. Please use the 'overrideConfig.parser' option instead.", | |
167 | "- 'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead.", | |
168 | "- 'rules' has been removed. Please use the 'overrideConfig.rules' option instead.", | |
169 | "- 'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead." | |
170 | ].join("\n")), "u") | |
171 | ); | |
172 | }); | |
173 | ||
174 | it("should throw readable messages if wrong type values are given to options", () => { | |
175 | assert.throws( | |
176 | () => new ESLint({ | |
177 | allowInlineConfig: "", | |
178 | baseConfig: "", | |
179 | cache: "", | |
180 | cacheLocation: "", | |
181 | cwd: "foo", | |
182 | errorOnUnmatchedPattern: "", | |
183 | extensions: "", | |
184 | fix: "", | |
185 | fixTypes: ["xyz"], | |
186 | globInputPaths: "", | |
187 | ignore: "", | |
188 | ignorePath: "", | |
189 | overrideConfig: "", | |
190 | overrideConfigFile: "", | |
191 | plugins: "", | |
192 | reportUnusedDisableDirectives: "", | |
193 | resolvePluginsRelativeTo: "", | |
194 | rulePaths: "", | |
195 | useEslintrc: "" | |
196 | }), | |
197 | new RegExp(escapeStringRegExp([ | |
198 | "Invalid Options:", | |
199 | "- 'allowInlineConfig' must be a boolean.", | |
200 | "- 'baseConfig' must be an object or null.", | |
201 | "- 'cache' must be a boolean.", | |
202 | "- 'cacheLocation' must be a non-empty string.", | |
203 | "- 'cwd' must be an absolute path.", | |
204 | "- 'errorOnUnmatchedPattern' must be a boolean.", | |
205 | "- 'extensions' must be an array of non-empty strings or null.", | |
206 | "- 'fix' must be a boolean or a function.", | |
609c276f | 207 | "- 'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".", |
56c4a2cb DC |
208 | "- 'globInputPaths' must be a boolean.", |
209 | "- 'ignore' must be a boolean.", | |
210 | "- 'ignorePath' must be a non-empty string or null.", | |
211 | "- 'overrideConfig' must be an object or null.", | |
212 | "- 'overrideConfigFile' must be a non-empty string or null.", | |
213 | "- 'plugins' must be an object or null.", | |
214 | "- 'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.", | |
215 | "- 'resolvePluginsRelativeTo' must be a non-empty string or null.", | |
216 | "- 'rulePaths' must be an array of non-empty strings.", | |
456be15e | 217 | "- 'useEslintrc' must be a boolean." |
56c4a2cb DC |
218 | ].join("\n")), "u") |
219 | ); | |
220 | }); | |
221 | ||
222 | it("should throw readable messages if 'plugins' option contains empty key", () => { | |
223 | assert.throws( | |
224 | () => new ESLint({ | |
225 | plugins: { | |
226 | "eslint-plugin-foo": {}, | |
227 | "eslint-plugin-bar": {}, | |
228 | "": {} | |
229 | } | |
230 | }), | |
231 | new RegExp(escapeStringRegExp([ | |
232 | "Invalid Options:", | |
233 | "- 'plugins' must not include an empty string." | |
234 | ].join("\n")), "u") | |
235 | ); | |
236 | }); | |
237 | }); | |
238 | ||
239 | describe("lintText()", () => { | |
240 | let eslint; | |
241 | ||
242 | it("should report the total and per file errors when using local cwd .eslintrc", async () => { | |
243 | eslint = new ESLint(); | |
244 | const results = await eslint.lintText("var foo = 'bar';"); | |
245 | ||
246 | assert.strictEqual(results.length, 1); | |
247 | assert.strictEqual(results[0].messages.length, 5); | |
248 | assert.strictEqual(results[0].messages[0].ruleId, "strict"); | |
249 | assert.strictEqual(results[0].messages[1].ruleId, "no-var"); | |
250 | assert.strictEqual(results[0].messages[2].ruleId, "no-unused-vars"); | |
251 | assert.strictEqual(results[0].messages[3].ruleId, "quotes"); | |
252 | assert.strictEqual(results[0].messages[4].ruleId, "eol-last"); | |
253 | assert.strictEqual(results[0].fixableErrorCount, 3); | |
254 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
255 | assert.strictEqual(results[0].usedDeprecatedRules.length, 0); | |
256 | }); | |
257 | ||
258 | it("should report the total and per file warnings when using local cwd .eslintrc", async () => { | |
259 | eslint = new ESLint({ | |
260 | overrideConfig: { | |
261 | rules: { | |
262 | quotes: 1, | |
263 | "no-var": 1, | |
264 | "eol-last": 1, | |
265 | strict: 1, | |
266 | "no-unused-vars": 1 | |
267 | } | |
268 | } | |
269 | }); | |
270 | const results = await eslint.lintText("var foo = 'bar';"); | |
271 | ||
272 | assert.strictEqual(results.length, 1); | |
273 | assert.strictEqual(results[0].messages.length, 5); | |
274 | assert.strictEqual(results[0].messages[0].ruleId, "strict"); | |
275 | assert.strictEqual(results[0].messages[1].ruleId, "no-var"); | |
276 | assert.strictEqual(results[0].messages[2].ruleId, "no-unused-vars"); | |
277 | assert.strictEqual(results[0].messages[3].ruleId, "quotes"); | |
278 | assert.strictEqual(results[0].messages[4].ruleId, "eol-last"); | |
279 | assert.strictEqual(results[0].fixableErrorCount, 0); | |
280 | assert.strictEqual(results[0].fixableWarningCount, 3); | |
281 | assert.strictEqual(results[0].usedDeprecatedRules.length, 0); | |
282 | }); | |
283 | ||
284 | it("should report one message when using specific config file", async () => { | |
285 | eslint = new ESLint({ | |
286 | overrideConfigFile: "fixtures/configurations/quotes-error.json", | |
287 | useEslintrc: false, | |
288 | cwd: getFixturePath("..") | |
289 | }); | |
290 | const results = await eslint.lintText("var foo = 'bar';"); | |
291 | ||
292 | assert.strictEqual(results.length, 1); | |
293 | assert.strictEqual(results[0].messages.length, 1); | |
294 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
295 | assert.strictEqual(results[0].messages[0].output, void 0); | |
296 | assert.strictEqual(results[0].errorCount, 1); | |
297 | assert.strictEqual(results[0].fixableErrorCount, 1); | |
298 | assert.strictEqual(results[0].warningCount, 0); | |
299 | assert.strictEqual(results[0].usedDeprecatedRules.length, 0); | |
300 | }); | |
301 | ||
302 | it("should report the filename when passed in", async () => { | |
303 | eslint = new ESLint({ | |
304 | ignore: false, | |
305 | cwd: getFixturePath() | |
306 | }); | |
307 | const options = { filePath: "test.js" }; | |
308 | const results = await eslint.lintText("var foo = 'bar';", options); | |
309 | ||
310 | assert.strictEqual(results[0].filePath, getFixturePath("test.js")); | |
311 | }); | |
312 | ||
313 | it("should return a warning when given a filename by --stdin-filename in excluded files list if warnIgnored is true", async () => { | |
314 | eslint = new ESLint({ | |
315 | ignorePath: getFixturePath(".eslintignore"), | |
316 | cwd: getFixturePath("..") | |
317 | }); | |
318 | const options = { filePath: "fixtures/passing.js", warnIgnored: true }; | |
319 | const results = await eslint.lintText("var bar = foo;", options); | |
320 | ||
321 | assert.strictEqual(results.length, 1); | |
322 | assert.strictEqual(results[0].filePath, getFixturePath("passing.js")); | |
323 | assert.strictEqual(results[0].messages[0].severity, 1); | |
324 | assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); | |
325 | assert.strictEqual(results[0].messages[0].output, void 0); | |
326 | assert.strictEqual(results[0].errorCount, 0); | |
327 | assert.strictEqual(results[0].warningCount, 1); | |
8f9d1d4d | 328 | assert.strictEqual(results[0].fatalErrorCount, 0); |
56c4a2cb DC |
329 | assert.strictEqual(results[0].fixableErrorCount, 0); |
330 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
331 | assert.strictEqual(results[0].usedDeprecatedRules.length, 0); | |
332 | }); | |
333 | ||
334 | it("should not return a warning when given a filename by --stdin-filename in excluded files list if warnIgnored is false", async () => { | |
335 | eslint = new ESLint({ | |
336 | ignorePath: getFixturePath(".eslintignore"), | |
337 | cwd: getFixturePath("..") | |
338 | }); | |
339 | const options = { | |
340 | filePath: "fixtures/passing.js", | |
341 | warnIgnored: false | |
342 | }; | |
343 | ||
344 | // intentional parsing error | |
345 | const results = await eslint.lintText("va r bar = foo;", options); | |
346 | ||
347 | // should not report anything because the file is ignored | |
348 | assert.strictEqual(results.length, 0); | |
349 | }); | |
350 | ||
351 | it("should suppress excluded file warnings by default", async () => { | |
352 | eslint = new ESLint({ | |
353 | ignorePath: getFixturePath(".eslintignore"), | |
354 | cwd: getFixturePath("..") | |
355 | }); | |
356 | const options = { filePath: "fixtures/passing.js" }; | |
357 | const results = await eslint.lintText("var bar = foo;", options); | |
358 | ||
359 | // should not report anything because there are no errors | |
360 | assert.strictEqual(results.length, 0); | |
361 | }); | |
362 | ||
363 | it("should return a message when given a filename by --stdin-filename in excluded files list and ignore is off", async () => { | |
364 | eslint = new ESLint({ | |
365 | ignorePath: "fixtures/.eslintignore", | |
366 | cwd: getFixturePath(".."), | |
367 | ignore: false, | |
368 | useEslintrc: false, | |
369 | overrideConfig: { | |
370 | rules: { | |
371 | "no-undef": 2 | |
372 | } | |
373 | } | |
374 | }); | |
375 | const options = { filePath: "fixtures/passing.js" }; | |
376 | const results = await eslint.lintText("var bar = foo;", options); | |
377 | ||
378 | assert.strictEqual(results.length, 1); | |
379 | assert.strictEqual(results[0].filePath, getFixturePath("passing.js")); | |
380 | assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); | |
381 | assert.strictEqual(results[0].messages[0].severity, 2); | |
382 | assert.strictEqual(results[0].messages[0].output, void 0); | |
383 | }); | |
384 | ||
385 | it("should return a message and fixed text when in fix mode", async () => { | |
386 | eslint = new ESLint({ | |
387 | useEslintrc: false, | |
388 | fix: true, | |
389 | overrideConfig: { | |
390 | rules: { | |
391 | semi: 2 | |
392 | } | |
393 | }, | |
394 | ignore: false, | |
395 | cwd: getFixturePath() | |
396 | }); | |
397 | const options = { filePath: "passing.js" }; | |
398 | const results = await eslint.lintText("var bar = foo", options); | |
399 | ||
400 | assert.deepStrictEqual(results, [ | |
401 | { | |
402 | filePath: getFixturePath("passing.js"), | |
403 | messages: [], | |
8f9d1d4d | 404 | suppressedMessages: [], |
56c4a2cb DC |
405 | errorCount: 0, |
406 | warningCount: 0, | |
609c276f | 407 | fatalErrorCount: 0, |
56c4a2cb DC |
408 | fixableErrorCount: 0, |
409 | fixableWarningCount: 0, | |
410 | output: "var bar = foo;", | |
411 | usedDeprecatedRules: [] | |
412 | } | |
413 | ]); | |
414 | }); | |
415 | ||
8f9d1d4d DC |
416 | it("should use eslint:recommended rules when eslint:recommended configuration is specified", async () => { |
417 | eslint = new ESLint({ | |
418 | useEslintrc: false, | |
419 | overrideConfig: { | |
420 | extends: ["eslint:recommended"] | |
421 | }, | |
422 | ignore: false, | |
423 | cwd: getFixturePath() | |
424 | }); | |
425 | const options = { filePath: "file.js" }; | |
426 | const results = await eslint.lintText("foo ()", options); | |
427 | ||
428 | assert.strictEqual(results.length, 1); | |
429 | assert.strictEqual(results[0].messages.length, 1); | |
430 | assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); | |
431 | assert.strictEqual(results[0].messages[0].severity, 2); | |
432 | }); | |
433 | ||
434 | it("should use eslint:all rules when eslint:all configuration is specified", async () => { | |
435 | eslint = new ESLint({ | |
436 | useEslintrc: false, | |
437 | overrideConfig: { | |
438 | extends: ["eslint:all"] | |
439 | }, | |
440 | ignore: false, | |
441 | cwd: getFixturePath() | |
442 | }); | |
443 | const options = { filePath: "file.js" }; | |
444 | const results = await eslint.lintText("foo ()", options); | |
445 | ||
446 | assert.strictEqual(results.length, 1); | |
447 | ||
448 | const { messages } = results[0]; | |
449 | ||
450 | // Some rules that should report errors in the given code. Not all, as we don't want to update this test when we add new rules. | |
451 | const expectedRules = ["no-undef", "semi", "func-call-spacing"]; | |
452 | ||
453 | expectedRules.forEach(ruleId => { | |
454 | const messageFromRule = messages.find(message => message.ruleId === ruleId); | |
455 | ||
456 | assert.ok( | |
457 | typeof messageFromRule === "object" && messageFromRule !== null, // LintMessage object | |
458 | `Expected a message from rule '${ruleId}'` | |
459 | ); | |
460 | assert.strictEqual(messageFromRule.severity, 2); | |
461 | }); | |
462 | ||
463 | }); | |
464 | ||
56c4a2cb DC |
465 | it("correctly autofixes semicolon-conflicting-fixes", async () => { |
466 | eslint = new ESLint({ | |
467 | cwd: path.join(fixtureDir, ".."), | |
468 | useEslintrc: false, | |
469 | fix: true | |
470 | }); | |
471 | const inputPath = getFixturePath("autofix/semicolon-conflicting-fixes.js"); | |
472 | const outputPath = getFixturePath("autofix/semicolon-conflicting-fixes.expected.js"); | |
473 | const results = await eslint.lintFiles([inputPath]); | |
474 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
475 | ||
476 | assert.strictEqual(results[0].output, expectedOutput); | |
477 | }); | |
478 | ||
479 | it("correctly autofixes return-conflicting-fixes", async () => { | |
480 | eslint = new ESLint({ | |
481 | cwd: path.join(fixtureDir, ".."), | |
482 | useEslintrc: false, | |
483 | fix: true | |
484 | }); | |
485 | const inputPath = getFixturePath("autofix/return-conflicting-fixes.js"); | |
486 | const outputPath = getFixturePath("autofix/return-conflicting-fixes.expected.js"); | |
487 | const results = await eslint.lintFiles([inputPath]); | |
488 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
489 | ||
490 | assert.strictEqual(results[0].output, expectedOutput); | |
491 | }); | |
492 | ||
493 | describe("Fix Types", () => { | |
494 | it("should throw an error when an invalid fix type is specified", () => { | |
495 | assert.throws(() => { | |
496 | eslint = new ESLint({ | |
497 | cwd: path.join(fixtureDir, ".."), | |
498 | useEslintrc: false, | |
499 | fix: true, | |
500 | fixTypes: ["layou"] | |
501 | }); | |
609c276f | 502 | }, /'fixTypes' must be an array of any of "directive", "problem", "suggestion", and "layout"\./iu); |
56c4a2cb DC |
503 | }); |
504 | ||
505 | it("should not fix any rules when fixTypes is used without fix", async () => { | |
506 | eslint = new ESLint({ | |
507 | cwd: path.join(fixtureDir, ".."), | |
508 | useEslintrc: false, | |
509 | fix: false, | |
510 | fixTypes: ["layout"] | |
511 | }); | |
512 | const inputPath = getFixturePath("fix-types/fix-only-semi.js"); | |
513 | const results = await eslint.lintFiles([inputPath]); | |
514 | ||
515 | assert.strictEqual(results[0].output, void 0); | |
516 | }); | |
517 | ||
518 | it("should not fix non-style rules when fixTypes has only 'layout'", async () => { | |
519 | eslint = new ESLint({ | |
520 | cwd: path.join(fixtureDir, ".."), | |
521 | useEslintrc: false, | |
522 | fix: true, | |
523 | fixTypes: ["layout"] | |
524 | }); | |
525 | const inputPath = getFixturePath("fix-types/fix-only-semi.js"); | |
526 | const outputPath = getFixturePath("fix-types/fix-only-semi.expected.js"); | |
527 | const results = await eslint.lintFiles([inputPath]); | |
528 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
529 | ||
530 | assert.strictEqual(results[0].output, expectedOutput); | |
531 | }); | |
532 | ||
533 | it("should not fix style or problem rules when fixTypes has only 'suggestion'", async () => { | |
534 | eslint = new ESLint({ | |
535 | cwd: path.join(fixtureDir, ".."), | |
536 | useEslintrc: false, | |
537 | fix: true, | |
538 | fixTypes: ["suggestion"] | |
539 | }); | |
540 | const inputPath = getFixturePath("fix-types/fix-only-prefer-arrow-callback.js"); | |
541 | const outputPath = getFixturePath("fix-types/fix-only-prefer-arrow-callback.expected.js"); | |
542 | const results = await eslint.lintFiles([inputPath]); | |
543 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
544 | ||
545 | assert.strictEqual(results[0].output, expectedOutput); | |
546 | }); | |
547 | ||
548 | it("should fix both style and problem rules when fixTypes has 'suggestion' and 'layout'", async () => { | |
549 | eslint = new ESLint({ | |
550 | cwd: path.join(fixtureDir, ".."), | |
551 | useEslintrc: false, | |
552 | fix: true, | |
553 | fixTypes: ["suggestion", "layout"] | |
554 | }); | |
555 | const inputPath = getFixturePath("fix-types/fix-both-semi-and-prefer-arrow-callback.js"); | |
556 | const outputPath = getFixturePath("fix-types/fix-both-semi-and-prefer-arrow-callback.expected.js"); | |
557 | const results = await eslint.lintFiles([inputPath]); | |
558 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
559 | ||
560 | assert.strictEqual(results[0].output, expectedOutput); | |
561 | }); | |
562 | ||
563 | it("should not throw an error when a rule doesn't have a 'meta' property", async () => { | |
564 | eslint = new ESLint({ | |
565 | cwd: path.join(fixtureDir, ".."), | |
566 | useEslintrc: false, | |
567 | fix: true, | |
568 | fixTypes: ["layout"], | |
569 | rulePaths: [getFixturePath("rules", "fix-types-test")] | |
570 | }); | |
571 | const inputPath = getFixturePath("fix-types/ignore-missing-meta.js"); | |
572 | const outputPath = getFixturePath("fix-types/ignore-missing-meta.expected.js"); | |
573 | const results = await eslint.lintFiles([inputPath]); | |
574 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
575 | ||
576 | assert.strictEqual(results[0].output, expectedOutput); | |
577 | }); | |
578 | ||
579 | it("should not throw an error when a rule is loaded after initialization with lintFiles()", async () => { | |
580 | eslint = new ESLint({ | |
581 | cwd: path.join(fixtureDir, ".."), | |
582 | useEslintrc: false, | |
583 | fix: true, | |
584 | fixTypes: ["layout"], | |
585 | plugins: { | |
586 | test: { | |
587 | rules: { | |
588 | "no-program": require(getFixturePath("rules", "fix-types-test", "no-program.js")) | |
589 | } | |
590 | } | |
591 | } | |
592 | }); | |
593 | const inputPath = getFixturePath("fix-types/ignore-missing-meta.js"); | |
594 | const outputPath = getFixturePath("fix-types/ignore-missing-meta.expected.js"); | |
595 | const results = await eslint.lintFiles([inputPath]); | |
596 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
597 | ||
598 | assert.strictEqual(results[0].output, expectedOutput); | |
599 | }); | |
600 | ||
601 | it("should not throw an error when a rule is loaded after initialization with lintText()", async () => { | |
602 | eslint = new ESLint({ | |
603 | cwd: path.join(fixtureDir, ".."), | |
604 | useEslintrc: false, | |
605 | fix: true, | |
606 | fixTypes: ["layout"], | |
607 | plugins: { | |
608 | test: { | |
609 | rules: { | |
610 | "no-program": require(getFixturePath("rules", "fix-types-test", "no-program.js")) | |
611 | } | |
612 | } | |
613 | } | |
614 | }); | |
615 | const inputPath = getFixturePath("fix-types/ignore-missing-meta.js"); | |
616 | const outputPath = getFixturePath("fix-types/ignore-missing-meta.expected.js"); | |
617 | const results = await eslint.lintText(fs.readFileSync(inputPath, { encoding: "utf8" }), { filePath: inputPath }); | |
618 | const expectedOutput = fs.readFileSync(outputPath, "utf8"); | |
619 | ||
620 | assert.strictEqual(results[0].output, expectedOutput); | |
621 | }); | |
622 | }); | |
623 | ||
624 | it("should return a message and omit fixed text when in fix mode and fixes aren't done", async () => { | |
625 | eslint = new ESLint({ | |
626 | useEslintrc: false, | |
627 | fix: true, | |
628 | overrideConfig: { | |
629 | rules: { | |
630 | "no-undef": 2 | |
631 | } | |
632 | }, | |
633 | ignore: false, | |
634 | cwd: getFixturePath() | |
635 | }); | |
636 | const options = { filePath: "passing.js" }; | |
637 | const results = await eslint.lintText("var bar = foo", options); | |
638 | ||
639 | assert.deepStrictEqual(results, [ | |
640 | { | |
641 | filePath: getFixturePath("passing.js"), | |
642 | messages: [ | |
643 | { | |
644 | ruleId: "no-undef", | |
645 | severity: 2, | |
646 | messageId: "undef", | |
647 | message: "'foo' is not defined.", | |
648 | line: 1, | |
649 | column: 11, | |
650 | endLine: 1, | |
651 | endColumn: 14, | |
652 | nodeType: "Identifier" | |
653 | } | |
654 | ], | |
8f9d1d4d | 655 | suppressedMessages: [], |
56c4a2cb DC |
656 | errorCount: 1, |
657 | warningCount: 0, | |
609c276f | 658 | fatalErrorCount: 0, |
56c4a2cb DC |
659 | fixableErrorCount: 0, |
660 | fixableWarningCount: 0, | |
661 | source: "var bar = foo", | |
662 | usedDeprecatedRules: [] | |
663 | } | |
664 | ]); | |
665 | }); | |
666 | ||
667 | it("should not delete code if there is a syntax error after trying to autofix.", async () => { | |
668 | eslint = eslintWithPlugins({ | |
669 | useEslintrc: false, | |
670 | fix: true, | |
671 | overrideConfig: { | |
672 | plugins: ["example"], | |
673 | rules: { | |
674 | "example/make-syntax-error": "error" | |
675 | } | |
676 | }, | |
677 | ignore: false, | |
678 | cwd: getFixturePath() | |
679 | }); | |
680 | const options = { filePath: "test.js" }; | |
681 | const results = await eslint.lintText("var bar = foo", options); | |
682 | ||
683 | assert.deepStrictEqual(results, [ | |
684 | { | |
685 | filePath: getFixturePath("test.js"), | |
686 | messages: [ | |
687 | { | |
688 | ruleId: null, | |
689 | fatal: true, | |
690 | severity: 2, | |
691 | message: "Parsing error: Unexpected token is", | |
692 | line: 1, | |
f2a92ac6 DC |
693 | column: 19, |
694 | nodeType: null | |
56c4a2cb DC |
695 | } |
696 | ], | |
8f9d1d4d | 697 | suppressedMessages: [], |
56c4a2cb DC |
698 | errorCount: 1, |
699 | warningCount: 0, | |
609c276f | 700 | fatalErrorCount: 1, |
56c4a2cb DC |
701 | fixableErrorCount: 0, |
702 | fixableWarningCount: 0, | |
703 | output: "var bar = foothis is a syntax error.", | |
704 | usedDeprecatedRules: [] | |
705 | } | |
706 | ]); | |
707 | }); | |
708 | ||
709 | it("should not crash even if there are any syntax error since the first time.", async () => { | |
710 | eslint = new ESLint({ | |
711 | useEslintrc: false, | |
712 | fix: true, | |
713 | overrideConfig: { | |
714 | rules: { | |
715 | "example/make-syntax-error": "error" | |
716 | } | |
717 | }, | |
718 | ignore: false, | |
719 | cwd: getFixturePath() | |
720 | }); | |
721 | const options = { filePath: "test.js" }; | |
722 | const results = await eslint.lintText("var bar =", options); | |
723 | ||
724 | assert.deepStrictEqual(results, [ | |
725 | { | |
726 | filePath: getFixturePath("test.js"), | |
727 | messages: [ | |
728 | { | |
729 | ruleId: null, | |
730 | fatal: true, | |
731 | severity: 2, | |
732 | message: "Parsing error: Unexpected token", | |
733 | line: 1, | |
f2a92ac6 DC |
734 | column: 10, |
735 | nodeType: null | |
56c4a2cb DC |
736 | } |
737 | ], | |
8f9d1d4d | 738 | suppressedMessages: [], |
56c4a2cb DC |
739 | errorCount: 1, |
740 | warningCount: 0, | |
609c276f | 741 | fatalErrorCount: 1, |
56c4a2cb DC |
742 | fixableErrorCount: 0, |
743 | fixableWarningCount: 0, | |
744 | source: "var bar =", | |
745 | usedDeprecatedRules: [] | |
746 | } | |
747 | ]); | |
748 | }); | |
749 | ||
750 | it("should return source code of file in `source` property when errors are present", async () => { | |
751 | eslint = new ESLint({ | |
752 | useEslintrc: false, | |
753 | overrideConfig: { | |
754 | rules: { semi: 2 } | |
755 | } | |
756 | }); | |
757 | const results = await eslint.lintText("var foo = 'bar'"); | |
758 | ||
759 | assert.strictEqual(results[0].source, "var foo = 'bar'"); | |
760 | }); | |
761 | ||
762 | it("should return source code of file in `source` property when warnings are present", async () => { | |
763 | eslint = new ESLint({ | |
764 | useEslintrc: false, | |
765 | overrideConfig: { | |
766 | rules: { semi: 1 } | |
767 | } | |
768 | }); | |
769 | const results = await eslint.lintText("var foo = 'bar'"); | |
770 | ||
771 | assert.strictEqual(results[0].source, "var foo = 'bar'"); | |
772 | }); | |
773 | ||
774 | ||
775 | it("should not return a `source` property when no errors or warnings are present", async () => { | |
776 | eslint = new ESLint({ | |
777 | useEslintrc: false, | |
778 | overrideConfig: { | |
779 | rules: { semi: 2 } | |
780 | } | |
781 | }); | |
782 | const results = await eslint.lintText("var foo = 'bar';"); | |
783 | ||
784 | assert.strictEqual(results[0].messages.length, 0); | |
785 | assert.strictEqual(results[0].source, void 0); | |
786 | }); | |
787 | ||
788 | it("should not return a `source` property when fixes are applied", async () => { | |
789 | eslint = new ESLint({ | |
790 | useEslintrc: false, | |
791 | fix: true, | |
792 | overrideConfig: { | |
793 | rules: { | |
794 | semi: 2, | |
795 | "no-unused-vars": 2 | |
796 | } | |
797 | } | |
798 | }); | |
799 | const results = await eslint.lintText("var msg = 'hi' + foo\n"); | |
800 | ||
801 | assert.strictEqual(results[0].source, void 0); | |
802 | assert.strictEqual(results[0].output, "var msg = 'hi' + foo;\n"); | |
803 | }); | |
804 | ||
805 | it("should return a `source` property when a parsing error has occurred", async () => { | |
806 | eslint = new ESLint({ | |
807 | useEslintrc: false, | |
808 | overrideConfig: { | |
809 | rules: { semi: 2 } | |
810 | } | |
811 | }); | |
812 | const results = await eslint.lintText("var bar = foothis is a syntax error.\n return bar;"); | |
813 | ||
814 | assert.deepStrictEqual(results, [ | |
815 | { | |
816 | filePath: "<text>", | |
817 | messages: [ | |
818 | { | |
819 | ruleId: null, | |
820 | fatal: true, | |
821 | severity: 2, | |
822 | message: "Parsing error: Unexpected token is", | |
823 | line: 1, | |
f2a92ac6 DC |
824 | column: 19, |
825 | nodeType: null | |
56c4a2cb DC |
826 | } |
827 | ], | |
8f9d1d4d | 828 | suppressedMessages: [], |
56c4a2cb DC |
829 | errorCount: 1, |
830 | warningCount: 0, | |
609c276f | 831 | fatalErrorCount: 1, |
56c4a2cb DC |
832 | fixableErrorCount: 0, |
833 | fixableWarningCount: 0, | |
834 | source: "var bar = foothis is a syntax error.\n return bar;", | |
835 | usedDeprecatedRules: [] | |
836 | } | |
837 | ]); | |
838 | }); | |
839 | ||
840 | // https://github.com/eslint/eslint/issues/5547 | |
841 | it("should respect default ignore rules, even with --no-ignore", async () => { | |
842 | eslint = new ESLint({ | |
843 | cwd: getFixturePath(), | |
844 | ignore: false | |
845 | }); | |
846 | const results = await eslint.lintText("var bar = foo;", { filePath: "node_modules/passing.js", warnIgnored: true }); | |
847 | const expectedMsg = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override."; | |
848 | ||
849 | assert.strictEqual(results.length, 1); | |
850 | assert.strictEqual(results[0].filePath, getFixturePath("node_modules/passing.js")); | |
851 | assert.strictEqual(results[0].messages[0].message, expectedMsg); | |
852 | }); | |
853 | ||
854 | describe('plugin shorthand notation ("@scope" for "@scope/eslint-plugin")', () => { | |
855 | const Module = require("module"); | |
856 | let originalFindPath = null; | |
857 | ||
609c276f | 858 | /* eslint-disable no-underscore-dangle -- Override Node API */ |
56c4a2cb DC |
859 | before(() => { |
860 | originalFindPath = Module._findPath; | |
861 | Module._findPath = function(id, ...otherArgs) { | |
862 | if (id === "@scope/eslint-plugin") { | |
863 | return path.resolve(__dirname, "../../fixtures/plugin-shorthand/basic/node_modules/@scope/eslint-plugin/index.js"); | |
864 | } | |
865 | return originalFindPath.call(this, id, ...otherArgs); | |
866 | }; | |
867 | }); | |
868 | after(() => { | |
869 | Module._findPath = originalFindPath; | |
870 | }); | |
609c276f | 871 | /* eslint-enable no-underscore-dangle -- Override Node API */ |
56c4a2cb DC |
872 | |
873 | it("should resolve 'plugins:[\"@scope\"]' to 'node_modules/@scope/eslint-plugin'.", async () => { | |
874 | eslint = new ESLint({ cwd: getFixturePath("plugin-shorthand/basic") }); | |
875 | const [result] = await eslint.lintText("var x = 0", { filePath: "index.js" }); | |
876 | ||
877 | assert.strictEqual(result.filePath, getFixturePath("plugin-shorthand/basic/index.js")); | |
878 | assert.strictEqual(result.messages[0].ruleId, "@scope/rule"); | |
879 | assert.strictEqual(result.messages[0].message, "OK"); | |
880 | }); | |
881 | ||
882 | it("should resolve 'extends:[\"plugin:@scope/recommended\"]' to 'node_modules/@scope/eslint-plugin'.", async () => { | |
883 | eslint = new ESLint({ cwd: getFixturePath("plugin-shorthand/extends") }); | |
884 | const [result] = await eslint.lintText("var x = 0", { filePath: "index.js" }); | |
885 | ||
886 | assert.strictEqual(result.filePath, getFixturePath("plugin-shorthand/extends/index.js")); | |
887 | assert.strictEqual(result.messages[0].ruleId, "@scope/rule"); | |
888 | assert.strictEqual(result.messages[0].message, "OK"); | |
889 | }); | |
890 | }); | |
891 | ||
892 | it("should warn when deprecated rules are found in a config", async () => { | |
893 | eslint = new ESLint({ | |
894 | cwd: originalDir, | |
895 | useEslintrc: false, | |
896 | overrideConfigFile: "tests/fixtures/cli-engine/deprecated-rule-config/.eslintrc.yml" | |
897 | }); | |
898 | const [result] = await eslint.lintText("foo"); | |
899 | ||
900 | assert.deepStrictEqual( | |
901 | result.usedDeprecatedRules, | |
902 | [{ ruleId: "indent-legacy", replacedBy: ["indent"] }] | |
903 | ); | |
904 | }); | |
905 | ||
906 | it("should throw if non-string value is given to 'code' parameter", async () => { | |
907 | eslint = new ESLint(); | |
908 | await assert.rejects(() => eslint.lintText(100), /'code' must be a string/u); | |
909 | }); | |
910 | ||
911 | it("should throw if non-object value is given to 'options' parameter", async () => { | |
912 | eslint = new ESLint(); | |
913 | await assert.rejects(() => eslint.lintText("var a = 0", "foo.js"), /'options' must be an object, null, or undefined/u); | |
914 | }); | |
915 | ||
916 | it("should throw if 'options' argument contains unknown key", async () => { | |
917 | eslint = new ESLint(); | |
609c276f | 918 | await assert.rejects(() => eslint.lintText("var a = 0", { filename: "foo.js" }), /'options' must not include the unknown option\(s\): filename/u); |
56c4a2cb DC |
919 | }); |
920 | ||
921 | it("should throw if non-string value is given to 'options.filePath' option", async () => { | |
922 | eslint = new ESLint(); | |
923 | await assert.rejects(() => eslint.lintText("var a = 0", { filePath: "" }), /'options.filePath' must be a non-empty string or undefined/u); | |
924 | }); | |
925 | ||
926 | it("should throw if non-boolean value is given to 'options.warnIgnored' option", async () => { | |
927 | eslint = new ESLint(); | |
928 | await assert.rejects(() => eslint.lintText("var a = 0", { warnIgnored: "" }), /'options.warnIgnored' must be a boolean or undefined/u); | |
929 | }); | |
930 | }); | |
931 | ||
932 | describe("lintFiles()", () => { | |
933 | ||
609c276f | 934 | /** @type {InstanceType<import("../../../lib/eslint").ESLint>} */ |
56c4a2cb DC |
935 | let eslint; |
936 | ||
937 | it("should use correct parser when custom parser is specified", async () => { | |
938 | eslint = new ESLint({ | |
939 | cwd: originalDir, | |
940 | ignore: false | |
941 | }); | |
942 | const filePath = path.resolve(__dirname, "../../fixtures/configurations/parser/custom.js"); | |
943 | const results = await eslint.lintFiles([filePath]); | |
944 | ||
945 | assert.strictEqual(results.length, 1); | |
946 | assert.strictEqual(results[0].messages.length, 1); | |
947 | assert.strictEqual(results[0].messages[0].message, "Parsing error: Boom!"); | |
948 | }); | |
949 | ||
950 | it("should report zero messages when given a config file and a valid file", async () => { | |
951 | eslint = new ESLint({ | |
952 | cwd: originalDir, | |
953 | overrideConfigFile: ".eslintrc.js" | |
954 | }); | |
955 | const results = await eslint.lintFiles(["lib/**/cli*.js"]); | |
956 | ||
957 | assert.strictEqual(results.length, 2); | |
958 | assert.strictEqual(results[0].messages.length, 0); | |
959 | assert.strictEqual(results[1].messages.length, 0); | |
960 | }); | |
961 | ||
962 | it("should handle multiple patterns with overlapping files", async () => { | |
963 | eslint = new ESLint({ | |
964 | cwd: originalDir, | |
965 | overrideConfigFile: ".eslintrc.js" | |
966 | }); | |
967 | const results = await eslint.lintFiles(["lib/**/cli*.js", "lib/cli.?s", "lib/{cli,cli-engine/cli-engine}.js"]); | |
968 | ||
969 | assert.strictEqual(results.length, 2); | |
970 | assert.strictEqual(results[0].messages.length, 0); | |
971 | assert.strictEqual(results[1].messages.length, 0); | |
972 | }); | |
973 | ||
974 | it("should report zero messages when given a config file and a valid file and espree as parser", async () => { | |
975 | eslint = new ESLint({ | |
976 | overrideConfig: { | |
977 | parser: "espree", | |
978 | parserOptions: { | |
6f036462 | 979 | ecmaVersion: 2021 |
56c4a2cb DC |
980 | } |
981 | }, | |
982 | useEslintrc: false | |
983 | }); | |
984 | const results = await eslint.lintFiles(["lib/cli.js"]); | |
985 | ||
986 | assert.strictEqual(results.length, 1); | |
987 | assert.strictEqual(results[0].messages.length, 0); | |
988 | }); | |
989 | ||
990 | it("should report zero messages when given a config file and a valid file and esprima as parser", async () => { | |
991 | eslint = new ESLint({ | |
992 | overrideConfig: { | |
993 | parser: "esprima" | |
994 | }, | |
995 | useEslintrc: false, | |
996 | ignore: false | |
997 | }); | |
998 | const results = await eslint.lintFiles(["tests/fixtures/passing.js"]); | |
999 | ||
1000 | assert.strictEqual(results.length, 1); | |
1001 | assert.strictEqual(results[0].messages.length, 0); | |
1002 | }); | |
1003 | ||
1004 | it("should throw an error when given a config file and a valid file and invalid parser", async () => { | |
1005 | eslint = new ESLint({ | |
1006 | overrideConfig: { | |
1007 | parser: "test11" | |
1008 | }, | |
1009 | useEslintrc: false | |
1010 | }); | |
1011 | ||
1012 | await assert.rejects(async () => await eslint.lintFiles(["lib/cli.js"]), /Cannot find module 'test11'/u); | |
1013 | }); | |
1014 | ||
1015 | it("should report zero messages when given a directory with a .js2 file", async () => { | |
1016 | eslint = new ESLint({ | |
1017 | cwd: path.join(fixtureDir, ".."), | |
1018 | extensions: [".js2"] | |
1019 | }); | |
1020 | const results = await eslint.lintFiles([getFixturePath("files/foo.js2")]); | |
1021 | ||
1022 | assert.strictEqual(results.length, 1); | |
1023 | assert.strictEqual(results[0].messages.length, 0); | |
1024 | }); | |
1025 | ||
1026 | it("should fall back to defaults when extensions is set to an empty array", async () => { | |
1027 | eslint = new ESLint({ | |
1028 | cwd: getFixturePath("configurations"), | |
1029 | overrideConfigFile: getFixturePath("configurations", "quotes-error.json"), | |
1030 | extensions: [] | |
1031 | }); | |
1032 | const results = await eslint.lintFiles([getFixturePath("single-quoted.js")]); | |
1033 | ||
1034 | assert.strictEqual(results.length, 1); | |
1035 | assert.strictEqual(results[0].messages.length, 1); | |
1036 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1037 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1038 | assert.strictEqual(results[0].errorCount, 1); | |
1039 | assert.strictEqual(results[0].warningCount, 0); | |
1040 | assert.strictEqual(results[0].fixableErrorCount, 1); | |
1041 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1042 | }); | |
1043 | ||
1044 | it("should report zero messages when given a directory with a .js and a .js2 file", async () => { | |
1045 | eslint = new ESLint({ | |
1046 | extensions: [".js", ".js2"], | |
1047 | ignore: false, | |
1048 | cwd: getFixturePath("..") | |
1049 | }); | |
1050 | const results = await eslint.lintFiles(["fixtures/files/"]); | |
1051 | ||
1052 | assert.strictEqual(results.length, 2); | |
1053 | assert.strictEqual(results[0].messages.length, 0); | |
1054 | assert.strictEqual(results[1].messages.length, 0); | |
1055 | }); | |
1056 | ||
1057 | it("should report zero messages when given a '**' pattern with a .js and a .js2 file", async () => { | |
1058 | eslint = new ESLint({ | |
1059 | extensions: [".js", ".js2"], | |
1060 | ignore: false, | |
1061 | cwd: path.join(fixtureDir, "..") | |
1062 | }); | |
1063 | const results = await eslint.lintFiles(["fixtures/files/*"]); | |
1064 | ||
1065 | assert.strictEqual(results.length, 2); | |
1066 | assert.strictEqual(results[0].messages.length, 0); | |
1067 | assert.strictEqual(results[1].messages.length, 0); | |
1068 | }); | |
1069 | ||
1070 | it("should resolve globs when 'globInputPaths' option is true", async () => { | |
1071 | eslint = new ESLint({ | |
1072 | extensions: [".js", ".js2"], | |
1073 | ignore: false, | |
1074 | cwd: getFixturePath("..") | |
1075 | }); | |
1076 | const results = await eslint.lintFiles(["fixtures/files/*"]); | |
1077 | ||
1078 | assert.strictEqual(results.length, 2); | |
1079 | assert.strictEqual(results[0].messages.length, 0); | |
1080 | assert.strictEqual(results[1].messages.length, 0); | |
1081 | }); | |
1082 | ||
1083 | it("should not resolve globs when 'globInputPaths' option is false", async () => { | |
1084 | eslint = new ESLint({ | |
1085 | extensions: [".js", ".js2"], | |
1086 | ignore: false, | |
1087 | cwd: getFixturePath(".."), | |
1088 | globInputPaths: false | |
1089 | }); | |
1090 | ||
1091 | await assert.rejects(async () => { | |
1092 | await eslint.lintFiles(["fixtures/files/*"]); | |
1093 | }, /No files matching 'fixtures\/files\/\*' were found \(glob was disabled\)\./u); | |
1094 | }); | |
1095 | ||
1096 | it("should report on all files passed explicitly, even if ignored by default", async () => { | |
1097 | eslint = new ESLint({ | |
1098 | cwd: getFixturePath("cli-engine") | |
1099 | }); | |
1100 | const results = await eslint.lintFiles(["node_modules/foo.js"]); | |
1101 | const expectedMsg = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override."; | |
1102 | ||
1103 | assert.strictEqual(results.length, 1); | |
1104 | assert.strictEqual(results[0].errorCount, 0); | |
1105 | assert.strictEqual(results[0].warningCount, 1); | |
8f9d1d4d | 1106 | assert.strictEqual(results[0].fatalErrorCount, 0); |
56c4a2cb DC |
1107 | assert.strictEqual(results[0].fixableErrorCount, 0); |
1108 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1109 | assert.strictEqual(results[0].messages[0].message, expectedMsg); | |
1110 | }); | |
1111 | ||
1112 | it("should report on globs with explicit inclusion of dotfiles, even though ignored by default", async () => { | |
1113 | eslint = new ESLint({ | |
1114 | cwd: getFixturePath("cli-engine"), | |
1115 | overrideConfig: { | |
1116 | rules: { | |
1117 | quotes: [2, "single"] | |
1118 | } | |
1119 | } | |
1120 | }); | |
1121 | const results = await eslint.lintFiles(["hidden/.hiddenfolder/*.js"]); | |
1122 | ||
1123 | assert.strictEqual(results.length, 1); | |
1124 | assert.strictEqual(results[0].errorCount, 1); | |
1125 | assert.strictEqual(results[0].warningCount, 0); | |
1126 | assert.strictEqual(results[0].fixableErrorCount, 1); | |
1127 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1128 | }); | |
1129 | ||
1130 | it("should not check default ignored files without --no-ignore flag", async () => { | |
1131 | eslint = new ESLint({ | |
1132 | cwd: getFixturePath("cli-engine") | |
1133 | }); | |
1134 | ||
1135 | await assert.rejects(async () => { | |
1136 | await eslint.lintFiles(["node_modules"]); | |
1137 | }, /All files matched by 'node_modules' are ignored\./u); | |
1138 | }); | |
1139 | ||
1140 | // https://github.com/eslint/eslint/issues/5547 | |
1141 | it("should not check node_modules files even with --no-ignore flag", async () => { | |
1142 | eslint = new ESLint({ | |
1143 | cwd: getFixturePath("cli-engine"), | |
1144 | ignore: false | |
1145 | }); | |
1146 | ||
1147 | await assert.rejects(async () => { | |
1148 | await eslint.lintFiles(["node_modules"]); | |
1149 | }, /All files matched by 'node_modules' are ignored\./u); | |
1150 | }); | |
1151 | ||
1152 | it("should not check .hidden files if they are passed explicitly without --no-ignore flag", async () => { | |
1153 | eslint = new ESLint({ | |
1154 | cwd: getFixturePath(".."), | |
1155 | useEslintrc: false, | |
1156 | overrideConfig: { | |
1157 | rules: { | |
1158 | quotes: [2, "single"] | |
1159 | } | |
1160 | } | |
1161 | }); | |
1162 | const results = await eslint.lintFiles(["fixtures/files/.bar.js"]); | |
1163 | const expectedMsg = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override."; | |
1164 | ||
1165 | assert.strictEqual(results.length, 1); | |
1166 | assert.strictEqual(results[0].errorCount, 0); | |
1167 | assert.strictEqual(results[0].warningCount, 1); | |
8f9d1d4d | 1168 | assert.strictEqual(results[0].fatalErrorCount, 0); |
56c4a2cb DC |
1169 | assert.strictEqual(results[0].fixableErrorCount, 0); |
1170 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1171 | assert.strictEqual(results[0].messages[0].message, expectedMsg); | |
1172 | }); | |
1173 | ||
1174 | // https://github.com/eslint/eslint/issues/12873 | |
1175 | it("should not check files within a .hidden folder if they are passed explicitly without the --no-ignore flag", async () => { | |
1176 | eslint = new ESLint({ | |
1177 | cwd: getFixturePath("cli-engine"), | |
1178 | useEslintrc: false, | |
1179 | overrideConfig: { | |
1180 | rules: { | |
1181 | quotes: [2, "single"] | |
1182 | } | |
1183 | } | |
1184 | }); | |
1185 | const results = await eslint.lintFiles(["hidden/.hiddenfolder/double-quotes.js"]); | |
1186 | const expectedMsg = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override."; | |
1187 | ||
1188 | assert.strictEqual(results.length, 1); | |
1189 | assert.strictEqual(results[0].errorCount, 0); | |
1190 | assert.strictEqual(results[0].warningCount, 1); | |
8f9d1d4d | 1191 | assert.strictEqual(results[0].fatalErrorCount, 0); |
56c4a2cb DC |
1192 | assert.strictEqual(results[0].fixableErrorCount, 0); |
1193 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1194 | assert.strictEqual(results[0].messages[0].message, expectedMsg); | |
1195 | }); | |
1196 | ||
1197 | it("should check .hidden files if they are passed explicitly with --no-ignore flag", async () => { | |
1198 | eslint = new ESLint({ | |
1199 | cwd: getFixturePath(".."), | |
1200 | ignore: false, | |
1201 | useEslintrc: false, | |
1202 | overrideConfig: { | |
1203 | rules: { | |
1204 | quotes: [2, "single"] | |
1205 | } | |
1206 | } | |
1207 | }); | |
1208 | const results = await eslint.lintFiles(["fixtures/files/.bar.js"]); | |
1209 | ||
1210 | assert.strictEqual(results.length, 1); | |
1211 | assert.strictEqual(results[0].warningCount, 0); | |
1212 | assert.strictEqual(results[0].errorCount, 1); | |
1213 | assert.strictEqual(results[0].fixableErrorCount, 1); | |
1214 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1215 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1216 | }); | |
1217 | ||
1218 | it("should check .hidden files if they are unignored with an --ignore-pattern", async () => { | |
1219 | eslint = new ESLint({ | |
1220 | cwd: getFixturePath("cli-engine"), | |
1221 | ignore: true, | |
1222 | useEslintrc: false, | |
1223 | overrideConfig: { | |
1224 | ignorePatterns: "!.hidden*", | |
1225 | rules: { | |
1226 | quotes: [2, "single"] | |
1227 | } | |
1228 | } | |
1229 | }); | |
1230 | const results = await eslint.lintFiles(["hidden/"]); | |
1231 | ||
1232 | assert.strictEqual(results.length, 1); | |
1233 | assert.strictEqual(results[0].warningCount, 0); | |
1234 | assert.strictEqual(results[0].errorCount, 1); | |
1235 | assert.strictEqual(results[0].fixableErrorCount, 1); | |
1236 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1237 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1238 | }); | |
1239 | ||
1240 | it("should report zero messages when given a pattern with a .js and a .js2 file", async () => { | |
1241 | eslint = new ESLint({ | |
1242 | extensions: [".js", ".js2"], | |
1243 | ignore: false, | |
1244 | cwd: path.join(fixtureDir, "..") | |
1245 | }); | |
1246 | const results = await eslint.lintFiles(["fixtures/files/*.?s*"]); | |
1247 | ||
1248 | assert.strictEqual(results.length, 2); | |
1249 | assert.strictEqual(results[0].messages.length, 0); | |
1250 | assert.strictEqual(results[1].messages.length, 0); | |
1251 | }); | |
1252 | ||
1253 | it("should return one error message when given a config with rules with options and severity level set to error", async () => { | |
1254 | eslint = new ESLint({ | |
1255 | cwd: getFixturePath("configurations"), | |
1256 | overrideConfigFile: getFixturePath("configurations", "quotes-error.json") | |
1257 | }); | |
1258 | const results = await eslint.lintFiles([getFixturePath("single-quoted.js")]); | |
1259 | ||
1260 | assert.strictEqual(results.length, 1); | |
1261 | assert.strictEqual(results[0].messages.length, 1); | |
1262 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1263 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1264 | assert.strictEqual(results[0].errorCount, 1); | |
1265 | assert.strictEqual(results[0].warningCount, 0); | |
1266 | assert.strictEqual(results[0].fixableErrorCount, 1); | |
1267 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1268 | }); | |
1269 | ||
1270 | it("should return 3 messages when given a config file and a directory of 3 valid files", async () => { | |
1271 | eslint = new ESLint({ | |
1272 | cwd: path.join(fixtureDir, ".."), | |
1273 | overrideConfigFile: getFixturePath("configurations", "semi-error.json") | |
1274 | }); | |
34eeec05 TL |
1275 | const fixturePath = getFixturePath("formatters"); |
1276 | const results = await eslint.lintFiles([fixturePath]); | |
56c4a2cb | 1277 | |
34eeec05 TL |
1278 | assert.strictEqual(results.length, 5); |
1279 | assert.strictEqual(path.relative(fixturePath, results[0].filePath), "async.js"); | |
56c4a2cb DC |
1280 | assert.strictEqual(results[0].errorCount, 0); |
1281 | assert.strictEqual(results[0].warningCount, 0); | |
1282 | assert.strictEqual(results[0].fixableErrorCount, 0); | |
1283 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
34eeec05 TL |
1284 | assert.strictEqual(results[0].messages.length, 0); |
1285 | assert.strictEqual(path.relative(fixturePath, results[1].filePath), "broken.js"); | |
56c4a2cb DC |
1286 | assert.strictEqual(results[1].errorCount, 0); |
1287 | assert.strictEqual(results[1].warningCount, 0); | |
1288 | assert.strictEqual(results[1].fixableErrorCount, 0); | |
1289 | assert.strictEqual(results[1].fixableWarningCount, 0); | |
34eeec05 TL |
1290 | assert.strictEqual(results[1].messages.length, 0); |
1291 | assert.strictEqual(path.relative(fixturePath, results[2].filePath), "cwd.js"); | |
56c4a2cb DC |
1292 | assert.strictEqual(results[2].errorCount, 0); |
1293 | assert.strictEqual(results[2].warningCount, 0); | |
1294 | assert.strictEqual(results[2].fixableErrorCount, 0); | |
1295 | assert.strictEqual(results[2].fixableWarningCount, 0); | |
34eeec05 TL |
1296 | assert.strictEqual(results[2].messages.length, 0); |
1297 | assert.strictEqual(path.relative(fixturePath, results[3].filePath), "simple.js"); | |
1298 | assert.strictEqual(results[3].errorCount, 0); | |
1299 | assert.strictEqual(results[3].warningCount, 0); | |
1300 | assert.strictEqual(results[3].fixableErrorCount, 0); | |
1301 | assert.strictEqual(results[3].fixableWarningCount, 0); | |
1302 | assert.strictEqual(results[3].messages.length, 0); | |
1303 | assert.strictEqual(path.relative(fixturePath, results[4].filePath), path.join("test", "simple.js")); | |
1304 | assert.strictEqual(results[4].errorCount, 0); | |
1305 | assert.strictEqual(results[4].warningCount, 0); | |
1306 | assert.strictEqual(results[4].fixableErrorCount, 0); | |
1307 | assert.strictEqual(results[4].fixableWarningCount, 0); | |
1308 | assert.strictEqual(results[4].messages.length, 0); | |
56c4a2cb DC |
1309 | }); |
1310 | ||
1311 | it("should process when file is given by not specifying extensions", async () => { | |
1312 | eslint = new ESLint({ | |
1313 | ignore: false, | |
1314 | cwd: path.join(fixtureDir, "..") | |
1315 | }); | |
1316 | const results = await eslint.lintFiles(["fixtures/files/foo.js2"]); | |
1317 | ||
1318 | assert.strictEqual(results.length, 1); | |
1319 | assert.strictEqual(results[0].messages.length, 0); | |
1320 | }); | |
1321 | ||
1322 | it("should return zero messages when given a config with environment set to browser", async () => { | |
1323 | eslint = new ESLint({ | |
1324 | cwd: path.join(fixtureDir, ".."), | |
1325 | overrideConfigFile: getFixturePath("configurations", "env-browser.json") | |
1326 | }); | |
1327 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("globals-browser.js"))]); | |
1328 | ||
1329 | assert.strictEqual(results.length, 1); | |
1330 | assert.strictEqual(results[0].messages.length, 0); | |
1331 | }); | |
1332 | ||
1333 | it("should return zero messages when given an option to set environment to browser", async () => { | |
1334 | eslint = new ESLint({ | |
1335 | cwd: path.join(fixtureDir, ".."), | |
1336 | overrideConfig: { | |
1337 | env: { browser: true }, | |
1338 | rules: { | |
1339 | "no-alert": 0, | |
1340 | "no-undef": 2 | |
1341 | } | |
1342 | } | |
1343 | }); | |
1344 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("globals-browser.js"))]); | |
1345 | ||
1346 | assert.strictEqual(results.length, 1); | |
1347 | assert.strictEqual(results[0].messages.length, 0); | |
1348 | }); | |
1349 | ||
1350 | it("should return zero messages when given a config with environment set to Node.js", async () => { | |
1351 | eslint = new ESLint({ | |
1352 | cwd: path.join(fixtureDir, ".."), | |
1353 | overrideConfigFile: getFixturePath("configurations", "env-node.json") | |
1354 | }); | |
1355 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("globals-node.js"))]); | |
1356 | ||
1357 | assert.strictEqual(results.length, 1); | |
1358 | assert.strictEqual(results[0].messages.length, 0); | |
1359 | }); | |
1360 | ||
1361 | it("should not return results from previous call when calling more than once", async () => { | |
1362 | eslint = new ESLint({ | |
1363 | cwd: path.join(fixtureDir, ".."), | |
1364 | ignore: false, | |
1365 | overrideConfig: { | |
1366 | rules: { | |
1367 | semi: 2 | |
1368 | } | |
1369 | } | |
1370 | }); | |
1371 | const failFilePath = fs.realpathSync(getFixturePath("missing-semicolon.js")); | |
1372 | const passFilePath = fs.realpathSync(getFixturePath("passing.js")); | |
1373 | ||
1374 | let results = await eslint.lintFiles([failFilePath]); | |
1375 | ||
1376 | assert.strictEqual(results.length, 1); | |
1377 | assert.strictEqual(results[0].filePath, failFilePath); | |
1378 | assert.strictEqual(results[0].messages.length, 1); | |
1379 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); | |
1380 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1381 | ||
1382 | results = await eslint.lintFiles([passFilePath]); | |
1383 | assert.strictEqual(results.length, 1); | |
1384 | assert.strictEqual(results[0].filePath, passFilePath); | |
1385 | assert.strictEqual(results[0].messages.length, 0); | |
1386 | }); | |
1387 | ||
1388 | it("should throw an error when given a directory with all eslint excluded files in the directory", async () => { | |
1389 | eslint = new ESLint({ | |
1390 | ignorePath: getFixturePath(".eslintignore") | |
1391 | }); | |
1392 | ||
1393 | await assert.rejects(async () => { | |
1394 | await eslint.lintFiles([getFixturePath("./cli-engine/")]); | |
1395 | }, new RegExp(escapeStringRegExp(`All files matched by '${getFixturePath("./cli-engine/")}' are ignored.`), "u")); | |
1396 | }); | |
1397 | ||
1398 | it("should throw an error when all given files are ignored", async () => { | |
1399 | await assert.rejects(async () => { | |
1400 | await eslint.lintFiles(["tests/fixtures/cli-engine/"]); | |
1401 | }, /All files matched by 'tests\/fixtures\/cli-engine\/' are ignored\./u); | |
1402 | }); | |
1403 | ||
1404 | it("should throw an error when all given files are ignored even with a `./` prefix", async () => { | |
1405 | eslint = new ESLint({ | |
1406 | ignorePath: getFixturePath(".eslintignore") | |
1407 | }); | |
1408 | ||
1409 | await assert.rejects(async () => { | |
1410 | await eslint.lintFiles(["./tests/fixtures/cli-engine/"]); | |
1411 | }, /All files matched by '\.\/tests\/fixtures\/cli-engine\/' are ignored\./u); | |
1412 | }); | |
1413 | ||
1414 | // https://github.com/eslint/eslint/issues/3788 | |
1415 | it("should ignore one-level down node_modules when ignore file has 'node_modules/' in it", async () => { | |
1416 | eslint = new ESLint({ | |
1417 | ignorePath: getFixturePath("cli-engine", "nested_node_modules", ".eslintignore"), | |
1418 | useEslintrc: false, | |
1419 | overrideConfig: { | |
1420 | rules: { | |
1421 | quotes: [2, "double"] | |
1422 | } | |
1423 | }, | |
1424 | cwd: getFixturePath("cli-engine", "nested_node_modules") | |
1425 | }); | |
1426 | const results = await eslint.lintFiles(["."]); | |
1427 | ||
1428 | assert.strictEqual(results.length, 1); | |
1429 | assert.strictEqual(results[0].errorCount, 0); | |
1430 | assert.strictEqual(results[0].warningCount, 0); | |
1431 | assert.strictEqual(results[0].fixableErrorCount, 0); | |
1432 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1433 | }); | |
1434 | ||
1435 | // https://github.com/eslint/eslint/issues/3812 | |
1436 | it("should ignore all files and throw an error when tests/fixtures/ is in ignore file", async () => { | |
1437 | eslint = new ESLint({ | |
1438 | ignorePath: getFixturePath("cli-engine/.eslintignore2"), | |
1439 | useEslintrc: false, | |
1440 | overrideConfig: { | |
1441 | rules: { | |
1442 | quotes: [2, "double"] | |
1443 | } | |
1444 | } | |
1445 | }); | |
1446 | ||
1447 | await assert.rejects(async () => { | |
1448 | await eslint.lintFiles(["./tests/fixtures/cli-engine/"]); | |
1449 | }, /All files matched by '\.\/tests\/fixtures\/cli-engine\/' are ignored\./u); | |
1450 | }); | |
1451 | ||
8f9d1d4d DC |
1452 | // https://github.com/eslint/eslint/issues/15642 |
1453 | it("should ignore files that are ignored by patterns with escaped brackets", async () => { | |
1454 | eslint = new ESLint({ | |
1455 | ignorePath: getFixturePath("ignored-paths", ".eslintignoreWithEscapedBrackets"), | |
1456 | useEslintrc: false, | |
1457 | cwd: getFixturePath("ignored-paths") | |
1458 | }); | |
1459 | ||
1460 | // Only `brackets/index.js` should be linted. Other files in `brackets/` should be ignored. | |
1461 | const results = await eslint.lintFiles(["brackets/*.js"]); | |
1462 | ||
1463 | assert.strictEqual(results.length, 1); | |
1464 | assert.strictEqual(results[0].filePath, getFixturePath("ignored-paths", "brackets", "index.js")); | |
1465 | }); | |
1466 | ||
56c4a2cb DC |
1467 | it("should throw an error when all given files are ignored via ignore-pattern", async () => { |
1468 | eslint = new ESLint({ | |
1469 | overrideConfig: { | |
1470 | ignorePatterns: "tests/fixtures/single-quoted.js" | |
1471 | } | |
1472 | }); | |
1473 | ||
1474 | await assert.rejects(async () => { | |
1475 | await eslint.lintFiles(["tests/fixtures/*-quoted.js"]); | |
1476 | }, /All files matched by 'tests\/fixtures\/\*-quoted\.js' are ignored\./u); | |
1477 | }); | |
1478 | ||
1479 | it("should return a warning when an explicitly given file is ignored", async () => { | |
1480 | eslint = new ESLint({ | |
1481 | ignorePath: getFixturePath(".eslintignore"), | |
1482 | cwd: getFixturePath() | |
1483 | }); | |
1484 | const filePath = getFixturePath("passing.js"); | |
1485 | const results = await eslint.lintFiles([filePath]); | |
1486 | ||
1487 | assert.strictEqual(results.length, 1); | |
1488 | assert.strictEqual(results[0].filePath, filePath); | |
1489 | assert.strictEqual(results[0].messages[0].severity, 1); | |
1490 | assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); | |
1491 | assert.strictEqual(results[0].errorCount, 0); | |
1492 | assert.strictEqual(results[0].warningCount, 1); | |
8f9d1d4d | 1493 | assert.strictEqual(results[0].fatalErrorCount, 0); |
56c4a2cb DC |
1494 | assert.strictEqual(results[0].fixableErrorCount, 0); |
1495 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
1496 | }); | |
1497 | ||
1498 | it("should return two messages when given a file in excluded files list while ignore is off", async () => { | |
1499 | eslint = new ESLint({ | |
1500 | ignorePath: getFixturePath(".eslintignore"), | |
1501 | ignore: false, | |
1502 | overrideConfig: { | |
1503 | rules: { | |
1504 | "no-undef": 2 | |
1505 | } | |
1506 | } | |
1507 | }); | |
1508 | const filePath = fs.realpathSync(getFixturePath("undef.js")); | |
1509 | const results = await eslint.lintFiles([filePath]); | |
1510 | ||
1511 | assert.strictEqual(results.length, 1); | |
1512 | assert.strictEqual(results[0].filePath, filePath); | |
1513 | assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); | |
1514 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1515 | assert.strictEqual(results[0].messages[1].ruleId, "no-undef"); | |
1516 | assert.strictEqual(results[0].messages[1].severity, 2); | |
1517 | }); | |
1518 | ||
1519 | it("should return zero messages when executing a file with a shebang", async () => { | |
1520 | eslint = new ESLint({ | |
1521 | ignore: false | |
1522 | }); | |
1523 | const results = await eslint.lintFiles([getFixturePath("shebang.js")]); | |
1524 | ||
1525 | assert.strictEqual(results.length, 1); | |
1526 | assert.strictEqual(results[0].messages.length, 0); | |
1527 | }); | |
1528 | ||
1529 | it("should give a warning when loading a custom rule that doesn't exist", async () => { | |
1530 | eslint = new ESLint({ | |
1531 | ignore: false, | |
1532 | rulePaths: [getFixturePath("rules", "dir1")], | |
1533 | overrideConfigFile: getFixturePath("rules", "missing-rule.json") | |
1534 | }); | |
1535 | const results = await eslint.lintFiles([getFixturePath("rules", "test", "test-custom-rule.js")]); | |
1536 | ||
1537 | assert.strictEqual(results.length, 1); | |
1538 | assert.strictEqual(results[0].messages.length, 1); | |
1539 | assert.strictEqual(results[0].messages[0].ruleId, "missing-rule"); | |
1540 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1541 | assert.strictEqual(results[0].messages[0].message, "Definition for rule 'missing-rule' was not found."); | |
1542 | }); | |
1543 | ||
1544 | it("should throw an error when loading a bad custom rule", async () => { | |
1545 | eslint = new ESLint({ | |
1546 | ignore: false, | |
1547 | rulePaths: [getFixturePath("rules", "wrong")], | |
1548 | overrideConfigFile: getFixturePath("rules", "eslint.json") | |
1549 | }); | |
1550 | ||
1551 | ||
1552 | await assert.rejects(async () => { | |
1553 | await eslint.lintFiles([getFixturePath("rules", "test", "test-custom-rule.js")]); | |
1554 | }, /Error while loading rule 'custom-rule'/u); | |
1555 | }); | |
1556 | ||
1557 | it("should return one message when a custom rule matches a file", async () => { | |
1558 | eslint = new ESLint({ | |
1559 | ignore: false, | |
1560 | useEslintrc: false, | |
1561 | rulePaths: [getFixturePath("rules/")], | |
1562 | overrideConfigFile: getFixturePath("rules", "eslint.json") | |
1563 | }); | |
1564 | const filePath = fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js")); | |
1565 | const results = await eslint.lintFiles([filePath]); | |
1566 | ||
1567 | assert.strictEqual(results.length, 1); | |
1568 | assert.strictEqual(results[0].filePath, filePath); | |
1569 | assert.strictEqual(results[0].messages.length, 2); | |
1570 | assert.strictEqual(results[0].messages[0].ruleId, "custom-rule"); | |
1571 | assert.strictEqual(results[0].messages[0].severity, 1); | |
1572 | }); | |
1573 | ||
1574 | it("should load custom rule from the provided cwd", async () => { | |
1575 | const cwd = path.resolve(getFixturePath("rules")); | |
1576 | ||
1577 | eslint = new ESLint({ | |
1578 | ignore: false, | |
1579 | cwd, | |
1580 | rulePaths: ["./"], | |
1581 | overrideConfigFile: "eslint.json" | |
1582 | }); | |
1583 | const filePath = fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js")); | |
1584 | const results = await eslint.lintFiles([filePath]); | |
1585 | ||
1586 | assert.strictEqual(results.length, 1); | |
1587 | assert.strictEqual(results[0].filePath, filePath); | |
1588 | assert.strictEqual(results[0].messages.length, 2); | |
1589 | assert.strictEqual(results[0].messages[0].ruleId, "custom-rule"); | |
1590 | assert.strictEqual(results[0].messages[0].severity, 1); | |
1591 | }); | |
1592 | ||
1593 | it("should return messages when multiple custom rules match a file", async () => { | |
1594 | eslint = new ESLint({ | |
1595 | ignore: false, | |
1596 | rulePaths: [ | |
1597 | getFixturePath("rules", "dir1"), | |
1598 | getFixturePath("rules", "dir2") | |
1599 | ], | |
1600 | overrideConfigFile: getFixturePath("rules", "multi-rulesdirs.json") | |
1601 | }); | |
1602 | const filePath = fs.realpathSync(getFixturePath("rules", "test-multi-rulesdirs.js")); | |
1603 | const results = await eslint.lintFiles([filePath]); | |
1604 | ||
1605 | assert.strictEqual(results.length, 1); | |
1606 | assert.strictEqual(results[0].filePath, filePath); | |
1607 | assert.strictEqual(results[0].messages.length, 2); | |
1608 | assert.strictEqual(results[0].messages[0].ruleId, "no-literals"); | |
1609 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1610 | assert.strictEqual(results[0].messages[1].ruleId, "no-strings"); | |
1611 | assert.strictEqual(results[0].messages[1].severity, 2); | |
1612 | }); | |
1613 | ||
1614 | it("should return zero messages when executing without useEslintrc flag", async () => { | |
1615 | eslint = new ESLint({ | |
1616 | ignore: false, | |
1617 | useEslintrc: false | |
1618 | }); | |
1619 | const filePath = fs.realpathSync(getFixturePath("missing-semicolon.js")); | |
1620 | const results = await eslint.lintFiles([filePath]); | |
1621 | ||
1622 | assert.strictEqual(results.length, 1); | |
1623 | assert.strictEqual(results[0].filePath, filePath); | |
1624 | assert.strictEqual(results[0].messages.length, 0); | |
1625 | }); | |
1626 | ||
1627 | it("should return zero messages when executing without useEslintrc flag in Node.js environment", async () => { | |
1628 | eslint = new ESLint({ | |
1629 | ignore: false, | |
1630 | useEslintrc: false, | |
1631 | overrideConfig: { | |
1632 | env: { node: true } | |
1633 | } | |
1634 | }); | |
1635 | const filePath = fs.realpathSync(getFixturePath("process-exit.js")); | |
1636 | const results = await eslint.lintFiles([filePath]); | |
1637 | ||
1638 | assert.strictEqual(results.length, 1); | |
1639 | assert.strictEqual(results[0].filePath, filePath); | |
1640 | assert.strictEqual(results[0].messages.length, 0); | |
1641 | }); | |
1642 | ||
1643 | it("should return zero messages and ignore .eslintrc files when executing with no-eslintrc flag", async () => { | |
1644 | eslint = new ESLint({ | |
1645 | ignore: false, | |
1646 | useEslintrc: false, | |
1647 | overrideConfig: { | |
1648 | env: { node: true } | |
1649 | } | |
1650 | }); | |
1651 | const filePath = fs.realpathSync(getFixturePath("eslintrc", "quotes.js")); | |
1652 | const results = await eslint.lintFiles([filePath]); | |
1653 | ||
1654 | assert.strictEqual(results.length, 1); | |
1655 | assert.strictEqual(results[0].filePath, filePath); | |
1656 | assert.strictEqual(results[0].messages.length, 0); | |
1657 | }); | |
1658 | ||
1659 | it("should return zero messages and ignore package.json files when executing with no-eslintrc flag", async () => { | |
1660 | eslint = new ESLint({ | |
1661 | ignore: false, | |
1662 | useEslintrc: false, | |
1663 | overrideConfig: { | |
1664 | env: { node: true } | |
1665 | } | |
1666 | }); | |
1667 | const filePath = fs.realpathSync(getFixturePath("packagejson", "quotes.js")); | |
1668 | const results = await eslint.lintFiles([filePath]); | |
1669 | ||
1670 | assert.strictEqual(results.length, 1); | |
1671 | assert.strictEqual(results[0].filePath, filePath); | |
1672 | assert.strictEqual(results[0].messages.length, 0); | |
1673 | }); | |
1674 | ||
1675 | it("should warn when deprecated rules are configured", async () => { | |
1676 | eslint = new ESLint({ | |
1677 | cwd: originalDir, | |
1678 | overrideConfigFile: ".eslintrc.js", | |
1679 | overrideConfig: { | |
1680 | rules: { | |
1681 | "indent-legacy": 1, | |
1682 | "require-jsdoc": 1, | |
1683 | "valid-jsdoc": 1 | |
1684 | } | |
1685 | } | |
1686 | }); | |
1687 | const results = await eslint.lintFiles(["lib/cli*.js"]); | |
1688 | ||
1689 | assert.deepStrictEqual( | |
1690 | results[0].usedDeprecatedRules, | |
1691 | [ | |
1692 | { ruleId: "indent-legacy", replacedBy: ["indent"] }, | |
1693 | { ruleId: "require-jsdoc", replacedBy: [] }, | |
1694 | { ruleId: "valid-jsdoc", replacedBy: [] } | |
1695 | ] | |
1696 | ); | |
1697 | }); | |
1698 | ||
1699 | it("should not warn when deprecated rules are not configured", async () => { | |
1700 | eslint = new ESLint({ | |
1701 | cwd: originalDir, | |
1702 | overrideConfigFile: ".eslintrc.js", | |
1703 | overrideConfig: { | |
1704 | rules: { indent: 1, "valid-jsdoc": 0, "require-jsdoc": 0 } | |
1705 | } | |
1706 | }); | |
1707 | const results = await eslint.lintFiles(["lib/cli*.js"]); | |
1708 | ||
1709 | assert.deepStrictEqual(results[0].usedDeprecatedRules, []); | |
1710 | }); | |
1711 | ||
1712 | it("should warn when deprecated rules are found in a config", async () => { | |
1713 | eslint = new ESLint({ | |
1714 | cwd: originalDir, | |
1715 | overrideConfigFile: "tests/fixtures/cli-engine/deprecated-rule-config/.eslintrc.yml", | |
1716 | useEslintrc: false | |
1717 | }); | |
1718 | const results = await eslint.lintFiles(["lib/cli*.js"]); | |
1719 | ||
1720 | assert.deepStrictEqual( | |
1721 | results[0].usedDeprecatedRules, | |
1722 | [{ ruleId: "indent-legacy", replacedBy: ["indent"] }] | |
1723 | ); | |
1724 | }); | |
1725 | ||
1726 | describe("Fix Mode", () => { | |
1727 | it("should return fixed text on multiple files when in fix mode", async () => { | |
1728 | ||
1729 | /** | |
1730 | * Converts CRLF to LF in output. | |
1731 | * This is a workaround for git's autocrlf option on Windows. | |
1732 | * @param {Object} result A result object to convert. | |
1733 | * @returns {void} | |
1734 | */ | |
1735 | function convertCRLF(result) { | |
1736 | if (result && result.output) { | |
1737 | result.output = result.output.replace(/\r\n/gu, "\n"); | |
1738 | } | |
1739 | } | |
1740 | ||
1741 | eslint = new ESLint({ | |
1742 | cwd: path.join(fixtureDir, ".."), | |
1743 | useEslintrc: false, | |
1744 | fix: true, | |
1745 | overrideConfig: { | |
1746 | rules: { | |
1747 | semi: 2, | |
1748 | quotes: [2, "double"], | |
1749 | eqeqeq: 2, | |
1750 | "no-undef": 2, | |
1751 | "space-infix-ops": 2 | |
1752 | } | |
1753 | } | |
1754 | }); | |
1755 | const results = await eslint.lintFiles([path.resolve(fixtureDir, `${fixtureDir}/fixmode`)]); | |
1756 | ||
1757 | results.forEach(convertCRLF); | |
1758 | assert.deepStrictEqual(results, [ | |
1759 | { | |
1760 | filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/multipass.js")), | |
1761 | messages: [], | |
8f9d1d4d | 1762 | suppressedMessages: [], |
56c4a2cb DC |
1763 | errorCount: 0, |
1764 | warningCount: 0, | |
609c276f | 1765 | fatalErrorCount: 0, |
56c4a2cb DC |
1766 | fixableErrorCount: 0, |
1767 | fixableWarningCount: 0, | |
1768 | output: "true ? \"yes\" : \"no\";\n", | |
1769 | usedDeprecatedRules: [] | |
1770 | }, | |
1771 | { | |
1772 | filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/ok.js")), | |
1773 | messages: [], | |
8f9d1d4d | 1774 | suppressedMessages: [], |
56c4a2cb DC |
1775 | errorCount: 0, |
1776 | warningCount: 0, | |
609c276f | 1777 | fatalErrorCount: 0, |
56c4a2cb DC |
1778 | fixableErrorCount: 0, |
1779 | fixableWarningCount: 0, | |
1780 | usedDeprecatedRules: [] | |
1781 | }, | |
1782 | { | |
1783 | filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/quotes-semi-eqeqeq.js")), | |
1784 | messages: [ | |
1785 | { | |
1786 | column: 9, | |
1787 | line: 2, | |
1788 | endColumn: 11, | |
1789 | endLine: 2, | |
1790 | message: "Expected '===' and instead saw '=='.", | |
1791 | messageId: "unexpected", | |
1792 | nodeType: "BinaryExpression", | |
1793 | ruleId: "eqeqeq", | |
1794 | severity: 2 | |
1795 | } | |
1796 | ], | |
8f9d1d4d | 1797 | suppressedMessages: [], |
56c4a2cb DC |
1798 | errorCount: 1, |
1799 | warningCount: 0, | |
609c276f | 1800 | fatalErrorCount: 0, |
56c4a2cb DC |
1801 | fixableErrorCount: 0, |
1802 | fixableWarningCount: 0, | |
1803 | output: "var msg = \"hi\";\nif (msg == \"hi\") {\n\n}\n", | |
1804 | usedDeprecatedRules: [] | |
1805 | }, | |
1806 | { | |
1807 | filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/quotes.js")), | |
1808 | messages: [ | |
1809 | { | |
1810 | column: 18, | |
1811 | line: 1, | |
1812 | endColumn: 21, | |
1813 | endLine: 1, | |
1814 | messageId: "undef", | |
1815 | message: "'foo' is not defined.", | |
1816 | nodeType: "Identifier", | |
1817 | ruleId: "no-undef", | |
1818 | severity: 2 | |
1819 | } | |
1820 | ], | |
8f9d1d4d | 1821 | suppressedMessages: [], |
56c4a2cb DC |
1822 | errorCount: 1, |
1823 | warningCount: 0, | |
609c276f | 1824 | fatalErrorCount: 0, |
56c4a2cb DC |
1825 | fixableErrorCount: 0, |
1826 | fixableWarningCount: 0, | |
1827 | output: "var msg = \"hi\" + foo;\n", | |
1828 | usedDeprecatedRules: [] | |
1829 | } | |
1830 | ]); | |
1831 | }); | |
1832 | ||
1833 | it("should run autofix even if files are cached without autofix results", async () => { | |
1834 | const baseOptions = { | |
1835 | cwd: path.join(fixtureDir, ".."), | |
1836 | useEslintrc: false, | |
1837 | overrideConfig: { | |
1838 | rules: { | |
1839 | semi: 2, | |
1840 | quotes: [2, "double"], | |
1841 | eqeqeq: 2, | |
1842 | "no-undef": 2, | |
1843 | "space-infix-ops": 2 | |
1844 | } | |
1845 | } | |
1846 | }; | |
1847 | ||
1848 | eslint = new ESLint(Object.assign({}, baseOptions, { cache: true, fix: false })); | |
1849 | ||
1850 | // Do initial lint run and populate the cache file | |
1851 | await eslint.lintFiles([path.resolve(fixtureDir, `${fixtureDir}/fixmode`)]); | |
1852 | ||
1853 | eslint = new ESLint(Object.assign({}, baseOptions, { cache: true, fix: true })); | |
1854 | const results = await eslint.lintFiles([path.resolve(fixtureDir, `${fixtureDir}/fixmode`)]); | |
1855 | ||
1856 | assert(results.some(result => result.output)); | |
1857 | }); | |
1858 | }); | |
1859 | ||
1860 | // These tests have to do with https://github.com/eslint/eslint/issues/963 | |
1861 | ||
1862 | describe("configuration hierarchy", () => { | |
1863 | ||
1864 | // Default configuration - blank | |
1865 | it("should return zero messages when executing with no .eslintrc", async () => { | |
1866 | eslint = new ESLint({ | |
1867 | cwd: path.join(fixtureDir, ".."), | |
1868 | useEslintrc: false | |
1869 | }); | |
1870 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); | |
1871 | ||
1872 | assert.strictEqual(results.length, 1); | |
1873 | assert.strictEqual(results[0].messages.length, 0); | |
1874 | }); | |
1875 | ||
1876 | // No default configuration rules - conf/environments.js (/*eslint-env node*/) | |
1877 | it("should return zero messages when executing with no .eslintrc in the Node.js environment", async () => { | |
1878 | eslint = new ESLint({ | |
1879 | cwd: path.join(fixtureDir, ".."), | |
1880 | useEslintrc: false | |
1881 | }); | |
1882 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes-node.js`)]); | |
1883 | ||
1884 | assert.strictEqual(results.length, 1); | |
1885 | assert.strictEqual(results[0].messages.length, 0); | |
1886 | }); | |
1887 | ||
1888 | // Project configuration - first level .eslintrc | |
1889 | it("should return zero messages when executing with .eslintrc in the Node.js environment", async () => { | |
1890 | eslint = new ESLint({ | |
1891 | cwd: path.join(fixtureDir, "..") | |
1892 | }); | |
1893 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/process-exit.js`)]); | |
1894 | ||
1895 | assert.strictEqual(results.length, 1); | |
1896 | assert.strictEqual(results[0].messages.length, 0); | |
1897 | }); | |
1898 | ||
1899 | // Project configuration - first level .eslintrc | |
1900 | it("should return zero messages when executing with .eslintrc in the Node.js environment", async () => { | |
1901 | eslint = new ESLint({ | |
1902 | cwd: path.join(fixtureDir, "..") | |
1903 | }); | |
1904 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/process-exit.js`)]); | |
1905 | ||
1906 | assert.strictEqual(results.length, 1); | |
1907 | assert.strictEqual(results[0].messages.length, 0); | |
1908 | }); | |
1909 | ||
1910 | // Project configuration - first level .eslintrc | |
1911 | it("should return one message when executing with .eslintrc", async () => { | |
1912 | eslint = new ESLint({ | |
1913 | cwd: path.join(fixtureDir, "..") | |
1914 | }); | |
1915 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); | |
1916 | ||
1917 | assert.strictEqual(results.length, 1); | |
1918 | assert.strictEqual(results[0].messages.length, 1); | |
1919 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1920 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1921 | }); | |
1922 | ||
1923 | // Project configuration - second level .eslintrc | |
1924 | it("should return one message when executing with local .eslintrc that overrides parent .eslintrc", async () => { | |
1925 | eslint = new ESLint({ | |
1926 | cwd: path.join(fixtureDir, "..") | |
1927 | }); | |
1928 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/console-wrong-quotes.js`)]); | |
1929 | ||
1930 | assert.strictEqual(results.length, 1); | |
1931 | assert.strictEqual(results[0].messages.length, 1); | |
1932 | assert.strictEqual(results[0].messages[0].ruleId, "no-console"); | |
1933 | assert.strictEqual(results[0].messages[0].severity, 1); | |
1934 | }); | |
1935 | ||
1936 | // Project configuration - third level .eslintrc | |
1937 | it("should return one message when executing with local .eslintrc that overrides parent and grandparent .eslintrc", async () => { | |
1938 | eslint = new ESLint({ | |
1939 | cwd: path.join(fixtureDir, "..") | |
1940 | }); | |
1941 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/subsubbroken/console-wrong-quotes.js`)]); | |
1942 | ||
1943 | assert.strictEqual(results.length, 1); | |
1944 | assert.strictEqual(results[0].messages.length, 1); | |
1945 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1946 | assert.strictEqual(results[0].messages[0].severity, 1); | |
1947 | }); | |
1948 | ||
1949 | // Project configuration - first level package.json | |
1950 | it("should return one message when executing with package.json", async () => { | |
1951 | eslint = new ESLint({ | |
1952 | cwd: path.join(fixtureDir, "..") | |
1953 | }); | |
1954 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/subdir/wrong-quotes.js`)]); | |
1955 | ||
1956 | assert.strictEqual(results.length, 1); | |
1957 | assert.strictEqual(results[0].messages.length, 1); | |
1958 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1959 | assert.strictEqual(results[0].messages[0].severity, 1); | |
1960 | }); | |
1961 | ||
1962 | // Project configuration - second level package.json | |
1963 | it("should return zero messages when executing with local package.json that overrides parent package.json", async () => { | |
1964 | eslint = new ESLint({ | |
1965 | cwd: path.join(fixtureDir, "..") | |
1966 | }); | |
1967 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/subdir/subsubdir/wrong-quotes.js`)]); | |
1968 | ||
1969 | assert.strictEqual(results.length, 1); | |
1970 | assert.strictEqual(results[0].messages.length, 0); | |
1971 | }); | |
1972 | ||
1973 | // Project configuration - third level package.json | |
1974 | it("should return one message when executing with local package.json that overrides parent and grandparent package.json", async () => { | |
1975 | eslint = new ESLint({ | |
1976 | cwd: path.join(fixtureDir, "..") | |
1977 | }); | |
1978 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/subdir/subsubdir/subsubsubdir/wrong-quotes.js`)]); | |
1979 | ||
1980 | assert.strictEqual(results.length, 1); | |
1981 | assert.strictEqual(results[0].messages.length, 1); | |
1982 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1983 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1984 | }); | |
1985 | ||
1986 | // Project configuration - .eslintrc overrides package.json in same directory | |
1987 | it("should return one message when executing with .eslintrc that overrides a package.json in the same directory", async () => { | |
1988 | eslint = new ESLint({ | |
1989 | cwd: path.join(fixtureDir, "..") | |
1990 | }); | |
1991 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/wrong-quotes.js`)]); | |
1992 | ||
1993 | assert.strictEqual(results.length, 1); | |
1994 | assert.strictEqual(results[0].messages.length, 1); | |
1995 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
1996 | assert.strictEqual(results[0].messages[0].severity, 2); | |
1997 | }); | |
1998 | ||
1999 | // Command line configuration - --config with first level .eslintrc | |
2000 | it("should return two messages when executing with config file that adds to local .eslintrc", async () => { | |
2001 | eslint = new ESLint({ | |
2002 | cwd: path.join(fixtureDir, ".."), | |
2003 | overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/add-conf.yaml` | |
2004 | }); | |
2005 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); | |
2006 | ||
2007 | assert.strictEqual(results.length, 1); | |
2008 | assert.strictEqual(results[0].messages.length, 2); | |
2009 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
2010 | assert.strictEqual(results[0].messages[0].severity, 2); | |
2011 | assert.strictEqual(results[0].messages[1].ruleId, "semi"); | |
2012 | assert.strictEqual(results[0].messages[1].severity, 1); | |
2013 | }); | |
2014 | ||
2015 | // Command line configuration - --config with first level .eslintrc | |
2016 | it("should return no messages when executing with config file that overrides local .eslintrc", async () => { | |
2017 | eslint = new ESLint({ | |
2018 | cwd: path.join(fixtureDir, ".."), | |
2019 | overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/override-conf.yaml` | |
2020 | }); | |
2021 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); | |
2022 | ||
2023 | assert.strictEqual(results.length, 1); | |
2024 | assert.strictEqual(results[0].messages.length, 0); | |
2025 | }); | |
2026 | ||
2027 | // Command line configuration - --config with second level .eslintrc | |
2028 | it("should return two messages when executing with config file that adds to local and parent .eslintrc", async () => { | |
2029 | eslint = new ESLint({ | |
2030 | cwd: path.join(fixtureDir, ".."), | |
2031 | overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/add-conf.yaml` | |
2032 | }); | |
2033 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/console-wrong-quotes.js`)]); | |
2034 | ||
2035 | assert.strictEqual(results.length, 1); | |
2036 | assert.strictEqual(results[0].messages.length, 2); | |
2037 | assert.strictEqual(results[0].messages[0].ruleId, "no-console"); | |
2038 | assert.strictEqual(results[0].messages[0].severity, 1); | |
2039 | assert.strictEqual(results[0].messages[1].ruleId, "semi"); | |
2040 | assert.strictEqual(results[0].messages[1].severity, 1); | |
2041 | }); | |
2042 | ||
2043 | // Command line configuration - --config with second level .eslintrc | |
2044 | it("should return one message when executing with config file that overrides local and parent .eslintrc", async () => { | |
2045 | eslint = new ESLint({ | |
2046 | cwd: path.join(fixtureDir, ".."), | |
2047 | overrideConfigFile: getFixturePath("config-hierarchy/broken/override-conf.yaml") | |
2048 | }); | |
2049 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/console-wrong-quotes.js`)]); | |
2050 | ||
2051 | assert.strictEqual(results.length, 1); | |
2052 | assert.strictEqual(results[0].messages.length, 1); | |
2053 | assert.strictEqual(results[0].messages[0].ruleId, "no-console"); | |
2054 | assert.strictEqual(results[0].messages[0].severity, 1); | |
2055 | }); | |
2056 | ||
2057 | // Command line configuration - --config with first level .eslintrc | |
2058 | it("should return no messages when executing with config file that overrides local .eslintrc", async () => { | |
2059 | eslint = new ESLint({ | |
2060 | cwd: path.join(fixtureDir, ".."), | |
2061 | overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/override-conf.yaml` | |
2062 | }); | |
2063 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); | |
2064 | ||
2065 | assert.strictEqual(results.length, 1); | |
2066 | assert.strictEqual(results[0].messages.length, 0); | |
2067 | }); | |
2068 | ||
2069 | // Command line configuration - --rule with --config and first level .eslintrc | |
2070 | it("should return one message when executing with command line rule and config file that overrides local .eslintrc", async () => { | |
2071 | eslint = new ESLint({ | |
2072 | cwd: path.join(fixtureDir, ".."), | |
2073 | overrideConfigFile: getFixturePath("config-hierarchy/broken/override-conf.yaml"), | |
2074 | overrideConfig: { | |
2075 | rules: { | |
2076 | quotes: [1, "double"] | |
2077 | } | |
2078 | } | |
2079 | }); | |
2080 | const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); | |
2081 | ||
2082 | assert.strictEqual(results.length, 1); | |
2083 | assert.strictEqual(results[0].messages.length, 1); | |
2084 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
2085 | assert.strictEqual(results[0].messages[0].severity, 1); | |
2086 | }); | |
2087 | ||
2088 | // Command line configuration - --rule with --config and first level .eslintrc | |
2089 | it("should return one message when executing with command line rule and config file that overrides local .eslintrc", async () => { | |
2090 | eslint = new ESLint({ | |
2091 | cwd: path.join(fixtureDir, ".."), | |
2092 | overrideConfigFile: getFixturePath("/config-hierarchy/broken/override-conf.yaml"), | |
2093 | overrideConfig: { | |
2094 | rules: { | |
2095 | quotes: [1, "double"] | |
2096 | } | |
2097 | } | |
2098 | }); | |
2099 | const results = await eslint.lintFiles([getFixturePath("config-hierarchy/broken/console-wrong-quotes.js")]); | |
2100 | ||
2101 | assert.strictEqual(results.length, 1); | |
2102 | assert.strictEqual(results[0].messages.length, 1); | |
2103 | assert.strictEqual(results[0].messages[0].ruleId, "quotes"); | |
2104 | assert.strictEqual(results[0].messages[0].severity, 1); | |
2105 | }); | |
2106 | }); | |
2107 | ||
2108 | describe("plugins", () => { | |
2109 | it("should return two messages when executing with config file that specifies a plugin", async () => { | |
2110 | eslint = eslintWithPlugins({ | |
2111 | cwd: path.join(fixtureDir, ".."), | |
2112 | overrideConfigFile: getFixturePath("configurations", "plugins-with-prefix.json"), | |
2113 | useEslintrc: false | |
2114 | }); | |
2115 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test/test-custom-rule.js"))]); | |
2116 | ||
2117 | assert.strictEqual(results.length, 1); | |
2118 | assert.strictEqual(results[0].messages.length, 2); | |
2119 | assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); | |
2120 | }); | |
2121 | ||
2122 | it("should return two messages when executing with config file that specifies a plugin with namespace", async () => { | |
2123 | eslint = eslintWithPlugins({ | |
2124 | cwd: path.join(fixtureDir, ".."), | |
2125 | overrideConfigFile: getFixturePath("configurations", "plugins-with-prefix-and-namespace.json"), | |
2126 | useEslintrc: false | |
2127 | }); | |
2128 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); | |
2129 | ||
2130 | assert.strictEqual(results.length, 1); | |
2131 | assert.strictEqual(results[0].messages.length, 2); | |
2132 | assert.strictEqual(results[0].messages[0].ruleId, "@eslint/example/example-rule"); | |
2133 | }); | |
2134 | ||
2135 | it("should return two messages when executing with config file that specifies a plugin without prefix", async () => { | |
2136 | eslint = eslintWithPlugins({ | |
2137 | cwd: path.join(fixtureDir, ".."), | |
2138 | overrideConfigFile: getFixturePath("configurations", "plugins-without-prefix.json"), | |
2139 | useEslintrc: false | |
2140 | }); | |
2141 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); | |
2142 | ||
2143 | assert.strictEqual(results.length, 1); | |
2144 | assert.strictEqual(results[0].messages.length, 2); | |
2145 | assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); | |
2146 | }); | |
2147 | ||
2148 | it("should return two messages when executing with config file that specifies a plugin without prefix and with namespace", async () => { | |
2149 | eslint = eslintWithPlugins({ | |
2150 | cwd: path.join(fixtureDir, ".."), | |
2151 | overrideConfigFile: getFixturePath("configurations", "plugins-without-prefix-with-namespace.json"), | |
2152 | useEslintrc: false | |
2153 | }); | |
2154 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); | |
2155 | ||
2156 | assert.strictEqual(results.length, 1); | |
2157 | assert.strictEqual(results[0].messages.length, 2); | |
2158 | assert.strictEqual(results[0].messages[0].ruleId, "@eslint/example/example-rule"); | |
2159 | }); | |
2160 | ||
2161 | it("should return two messages when executing with cli option that specifies a plugin", async () => { | |
2162 | eslint = eslintWithPlugins({ | |
2163 | cwd: path.join(fixtureDir, ".."), | |
2164 | useEslintrc: false, | |
2165 | overrideConfig: { | |
2166 | plugins: ["example"], | |
2167 | rules: { "example/example-rule": 1 } | |
2168 | } | |
2169 | }); | |
2170 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); | |
2171 | ||
2172 | assert.strictEqual(results.length, 1); | |
2173 | assert.strictEqual(results[0].messages.length, 2); | |
2174 | assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); | |
2175 | }); | |
2176 | ||
2177 | it("should return two messages when executing with cli option that specifies preloaded plugin", async () => { | |
2178 | eslint = new ESLint({ | |
2179 | cwd: path.join(fixtureDir, ".."), | |
2180 | useEslintrc: false, | |
2181 | overrideConfig: { | |
2182 | plugins: ["test"], | |
2183 | rules: { "test/example-rule": 1 } | |
2184 | }, | |
2185 | plugins: { | |
2186 | "eslint-plugin-test": { rules: { "example-rule": require("../../fixtures/rules/custom-rule") } } | |
2187 | } | |
2188 | }); | |
2189 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); | |
2190 | ||
2191 | assert.strictEqual(results.length, 1); | |
2192 | assert.strictEqual(results[0].messages.length, 2); | |
2193 | assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule"); | |
2194 | }); | |
2195 | ||
609c276f TL |
2196 | it("should return two messages when executing with `baseConfig` that extends preloaded plugin config", async () => { |
2197 | eslint = new ESLint({ | |
2198 | cwd: path.join(fixtureDir, ".."), | |
2199 | useEslintrc: false, | |
2200 | baseConfig: { | |
2201 | extends: ["plugin:test/preset"] | |
2202 | }, | |
2203 | plugins: { | |
2204 | test: { | |
2205 | rules: { | |
2206 | "example-rule": require("../../fixtures/rules/custom-rule") | |
2207 | }, | |
2208 | configs: { | |
2209 | preset: { | |
2210 | rules: { | |
2211 | "test/example-rule": 1 | |
2212 | }, | |
2213 | plugins: ["test"] | |
2214 | } | |
2215 | } | |
2216 | } | |
2217 | } | |
2218 | }); | |
2219 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); | |
2220 | ||
2221 | assert.strictEqual(results.length, 1); | |
2222 | assert.strictEqual(results[0].messages.length, 2); | |
2223 | assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule"); | |
2224 | }); | |
2225 | ||
56c4a2cb DC |
2226 | it("should load plugins from the `loadPluginsRelativeTo` directory, if specified", async () => { |
2227 | eslint = new ESLint({ | |
2228 | resolvePluginsRelativeTo: getFixturePath("plugins"), | |
2229 | baseConfig: { | |
2230 | plugins: ["with-rules"], | |
2231 | rules: { "with-rules/rule1": "error" } | |
2232 | }, | |
2233 | useEslintrc: false | |
2234 | }); | |
2235 | const results = await eslint.lintText("foo"); | |
2236 | ||
2237 | assert.strictEqual(results.length, 1); | |
2238 | assert.strictEqual(results[0].messages.length, 1); | |
2239 | assert.strictEqual(results[0].messages[0].ruleId, "with-rules/rule1"); | |
2240 | assert.strictEqual(results[0].messages[0].message, "Rule report from plugin"); | |
2241 | }); | |
2242 | }); | |
2243 | ||
2244 | describe("cache", () => { | |
2245 | ||
2246 | /** | |
2247 | * helper method to delete a file without caring about exceptions | |
2248 | * @param {string} filePath The file path | |
2249 | * @returns {void} | |
2250 | */ | |
2251 | function doDelete(filePath) { | |
2252 | try { | |
2253 | fs.unlinkSync(filePath); | |
d3726936 | 2254 | } catch { |
56c4a2cb DC |
2255 | |
2256 | /* | |
2257 | * we don't care if the file didn't exist, since our | |
2258 | * intention was to remove the file | |
2259 | */ | |
2260 | } | |
2261 | } | |
2262 | ||
2263 | /** | |
2264 | * helper method to delete the cache files created during testing | |
2265 | * @returns {void} | |
2266 | */ | |
2267 | function deleteCache() { | |
2268 | doDelete(path.resolve(".eslintcache")); | |
2269 | doDelete(path.resolve(".cache/custom-cache")); | |
2270 | } | |
2271 | ||
2272 | beforeEach(() => { | |
2273 | deleteCache(); | |
2274 | }); | |
2275 | ||
2276 | afterEach(() => { | |
2277 | sinon.restore(); | |
2278 | deleteCache(); | |
2279 | }); | |
2280 | ||
2281 | describe("when the cacheFile is a directory or looks like a directory", () => { | |
2282 | ||
2283 | /** | |
2284 | * helper method to delete the cache files created during testing | |
2285 | * @returns {void} | |
2286 | */ | |
2287 | function deleteCacheDir() { | |
2288 | try { | |
2289 | fs.unlinkSync("./tmp/.cacheFileDir/.cache_hashOfCurrentWorkingDirectory"); | |
d3726936 | 2290 | } catch { |
56c4a2cb DC |
2291 | |
2292 | /* | |
2293 | * we don't care if the file didn't exist, since our | |
2294 | * intention was to remove the file | |
2295 | */ | |
2296 | } | |
2297 | } | |
2298 | beforeEach(() => { | |
2299 | deleteCacheDir(); | |
2300 | }); | |
2301 | ||
2302 | afterEach(() => { | |
2303 | deleteCacheDir(); | |
2304 | }); | |
2305 | ||
2306 | it("should create the cache file inside the provided directory", async () => { | |
2307 | assert(!shell.test("-d", path.resolve("./tmp/.cacheFileDir/.cache_hashOfCurrentWorkingDirectory")), "the cache for eslint does not exist"); | |
2308 | ||
2309 | eslint = new ESLint({ | |
2310 | useEslintrc: false, | |
2311 | ||
2312 | // specifying cache true the cache will be created | |
2313 | cache: true, | |
2314 | cacheLocation: "./tmp/.cacheFileDir/", | |
2315 | overrideConfig: { | |
2316 | rules: { | |
2317 | "no-console": 0, | |
2318 | "no-unused-vars": 2 | |
2319 | } | |
2320 | }, | |
2321 | extensions: ["js"], | |
2322 | ignore: false | |
2323 | }); | |
2324 | const file = getFixturePath("cache/src", "test-file.js"); | |
2325 | ||
2326 | await eslint.lintFiles([file]); | |
2327 | ||
2328 | assert(shell.test("-f", path.resolve(`./tmp/.cacheFileDir/.cache_${hash(process.cwd())}`)), "the cache for eslint was created"); | |
2329 | ||
2330 | sinon.restore(); | |
2331 | }); | |
2332 | }); | |
2333 | ||
2334 | it("should create the cache file inside the provided directory using the cacheLocation option", async () => { | |
2335 | assert(!shell.test("-d", path.resolve("./tmp/.cacheFileDir/.cache_hashOfCurrentWorkingDirectory")), "the cache for eslint does not exist"); | |
2336 | ||
2337 | eslint = new ESLint({ | |
2338 | useEslintrc: false, | |
2339 | ||
2340 | // specifying cache true the cache will be created | |
2341 | cache: true, | |
2342 | cacheLocation: "./tmp/.cacheFileDir/", | |
2343 | overrideConfig: { | |
2344 | rules: { | |
2345 | "no-console": 0, | |
2346 | "no-unused-vars": 2 | |
2347 | } | |
2348 | }, | |
2349 | extensions: ["js"], | |
2350 | ignore: false | |
2351 | }); | |
2352 | const file = getFixturePath("cache/src", "test-file.js"); | |
2353 | ||
2354 | await eslint.lintFiles([file]); | |
2355 | ||
2356 | assert(shell.test("-f", path.resolve(`./tmp/.cacheFileDir/.cache_${hash(process.cwd())}`)), "the cache for eslint was created"); | |
2357 | ||
2358 | sinon.restore(); | |
2359 | }); | |
2360 | ||
2361 | it("should create the cache file inside cwd when no cacheLocation provided", async () => { | |
2362 | const cwd = path.resolve(getFixturePath("cli-engine")); | |
2363 | ||
2364 | eslint = new ESLint({ | |
2365 | useEslintrc: false, | |
2366 | cache: true, | |
2367 | cwd, | |
2368 | overrideConfig: { | |
2369 | rules: { | |
2370 | "no-console": 0 | |
2371 | } | |
2372 | }, | |
2373 | extensions: ["js"], | |
2374 | ignore: false | |
2375 | }); | |
2376 | const file = getFixturePath("cli-engine", "console.js"); | |
2377 | ||
2378 | await eslint.lintFiles([file]); | |
2379 | ||
2380 | assert(shell.test("-f", path.resolve(cwd, ".eslintcache")), "the cache for eslint was created at provided cwd"); | |
2381 | }); | |
2382 | ||
2383 | it("should invalidate the cache if the configuration changed between executions", async () => { | |
2384 | assert(!shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint does not exist"); | |
2385 | ||
2386 | eslint = new ESLint({ | |
2387 | useEslintrc: false, | |
2388 | ||
2389 | // specifying cache true the cache will be created | |
2390 | cache: true, | |
2391 | overrideConfig: { | |
2392 | rules: { | |
2393 | "no-console": 0, | |
2394 | "no-unused-vars": 2 | |
2395 | } | |
2396 | }, | |
2397 | extensions: ["js"], | |
2398 | ignore: false | |
2399 | }); | |
2400 | ||
2401 | let spy = sinon.spy(fs, "readFileSync"); | |
2402 | ||
2403 | let file = getFixturePath("cache/src", "test-file.js"); | |
2404 | ||
2405 | file = fs.realpathSync(file); | |
2406 | const results = await eslint.lintFiles([file]); | |
2407 | ||
2408 | for (const { errorCount, warningCount } of results) { | |
2409 | assert.strictEqual(errorCount + warningCount, 0, "the file passed without errors or warnings"); | |
2410 | } | |
2411 | assert.strictEqual(spy.getCall(0).args[0], file, "the module read the file because is considered changed"); | |
2412 | assert(shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint was created"); | |
2413 | ||
2414 | // destroy the spy | |
2415 | sinon.restore(); | |
2416 | ||
2417 | eslint = new ESLint({ | |
2418 | useEslintrc: false, | |
2419 | ||
2420 | // specifying cache true the cache will be created | |
2421 | cache: true, | |
2422 | overrideConfig: { | |
2423 | rules: { | |
2424 | "no-console": 2, | |
2425 | "no-unused-vars": 2 | |
2426 | } | |
2427 | }, | |
2428 | extensions: ["js"], | |
2429 | ignore: false | |
2430 | }); | |
2431 | ||
2432 | // create a new spy | |
2433 | spy = sinon.spy(fs, "readFileSync"); | |
2434 | ||
2435 | const [cachedResult] = await eslint.lintFiles([file]); | |
2436 | ||
2437 | assert.strictEqual(spy.getCall(0).args[0], file, "the module read the file because is considered changed because the config changed"); | |
2438 | assert.strictEqual(cachedResult.errorCount, 1, "since configuration changed the cache was not used an one error was reported"); | |
2439 | assert(shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint was created"); | |
2440 | }); | |
2441 | ||
2442 | it("should remember the files from a previous run and do not operate on them if not changed", async () => { | |
2443 | assert(!shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint does not exist"); | |
2444 | ||
2445 | eslint = new ESLint({ | |
2446 | useEslintrc: false, | |
2447 | ||
2448 | // specifying cache true the cache will be created | |
2449 | cache: true, | |
2450 | overrideConfig: { | |
2451 | rules: { | |
2452 | "no-console": 0, | |
2453 | "no-unused-vars": 2 | |
2454 | } | |
2455 | }, | |
2456 | extensions: ["js"], | |
2457 | ignore: false | |
2458 | }); | |
2459 | ||
2460 | let spy = sinon.spy(fs, "readFileSync"); | |
2461 | ||
2462 | let file = getFixturePath("cache/src", "test-file.js"); | |
2463 | ||
2464 | file = fs.realpathSync(file); | |
2465 | ||
2466 | const result = await eslint.lintFiles([file]); | |
2467 | ||
2468 | assert.strictEqual(spy.getCall(0).args[0], file, "the module read the file because is considered changed"); | |
2469 | assert(shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint was created"); | |
2470 | ||
2471 | // destroy the spy | |
2472 | sinon.restore(); | |
2473 | ||
2474 | eslint = new ESLint({ | |
2475 | useEslintrc: false, | |
2476 | ||
2477 | // specifying cache true the cache will be created | |
2478 | cache: true, | |
2479 | overrideConfig: { | |
2480 | rules: { | |
2481 | "no-console": 0, | |
2482 | "no-unused-vars": 2 | |
2483 | } | |
2484 | }, | |
2485 | extensions: ["js"], | |
2486 | ignore: false | |
2487 | }); | |
2488 | ||
2489 | // create a new spy | |
2490 | spy = sinon.spy(fs, "readFileSync"); | |
2491 | ||
2492 | const cachedResult = await eslint.lintFiles([file]); | |
2493 | ||
2494 | assert.deepStrictEqual(result, cachedResult, "the result is the same regardless of using cache or not"); | |
2495 | ||
2496 | // assert the file was not processed because the cache was used | |
2497 | assert(!spy.calledWith(file), "the file was not loaded because it used the cache"); | |
2498 | }); | |
2499 | ||
2500 | it("should remember the files from a previous run and do not operate on then if not changed", async () => { | |
2501 | const cacheLocation = getFixturePath(".eslintcache"); | |
2502 | const eslintOptions = { | |
2503 | useEslintrc: false, | |
2504 | ||
2505 | // specifying cache true the cache will be created | |
2506 | cache: true, | |
2507 | cacheLocation, | |
2508 | overrideConfig: { | |
2509 | rules: { | |
2510 | "no-console": 0, | |
2511 | "no-unused-vars": 2 | |
2512 | } | |
2513 | }, | |
2514 | extensions: ["js"], | |
2515 | cwd: path.join(fixtureDir, "..") | |
2516 | }; | |
2517 | ||
2518 | assert(!shell.test("-f", cacheLocation), "the cache for eslint does not exist"); | |
2519 | ||
2520 | eslint = new ESLint(eslintOptions); | |
2521 | ||
2522 | let file = getFixturePath("cache/src", "test-file.js"); | |
2523 | ||
2524 | file = fs.realpathSync(file); | |
2525 | ||
2526 | await eslint.lintFiles([file]); | |
2527 | ||
2528 | assert(shell.test("-f", cacheLocation), "the cache for eslint was created"); | |
2529 | ||
2530 | eslintOptions.cache = false; | |
2531 | eslint = new ESLint(eslintOptions); | |
2532 | ||
2533 | await eslint.lintFiles([file]); | |
2534 | ||
2535 | assert(!shell.test("-f", cacheLocation), "the cache for eslint was deleted since last run did not used the cache"); | |
2536 | }); | |
2537 | ||
2538 | it("should store in the cache a file that failed the test", async () => { | |
2539 | const cacheLocation = getFixturePath(".eslintcache"); | |
2540 | ||
2541 | assert(!shell.test("-f", cacheLocation), "the cache for eslint does not exist"); | |
2542 | ||
2543 | eslint = new ESLint({ | |
2544 | cwd: path.join(fixtureDir, ".."), | |
2545 | useEslintrc: false, | |
2546 | ||
2547 | // specifying cache true the cache will be created | |
2548 | cache: true, | |
2549 | cacheLocation, | |
2550 | overrideConfig: { | |
2551 | rules: { | |
2552 | "no-console": 0, | |
2553 | "no-unused-vars": 2 | |
2554 | } | |
2555 | }, | |
2556 | extensions: ["js"] | |
2557 | }); | |
2558 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2559 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2560 | const result = await eslint.lintFiles([badFile, goodFile]); | |
2561 | ||
2562 | assert(shell.test("-f", cacheLocation), "the cache for eslint was created"); | |
2563 | const fileCache = fCache.createFromFile(cacheLocation); | |
2564 | const { cache } = fileCache; | |
2565 | ||
2566 | assert.strictEqual(typeof cache.getKey(goodFile), "object", "the entry for the good file is in the cache"); | |
2567 | assert.strictEqual(typeof cache.getKey(badFile), "object", "the entry for the bad file is in the cache"); | |
2568 | const cachedResult = await eslint.lintFiles([badFile, goodFile]); | |
2569 | ||
2570 | assert.deepStrictEqual(result, cachedResult, "result is the same with or without cache"); | |
2571 | }); | |
2572 | ||
2573 | it("should not contain in the cache a file that was deleted", async () => { | |
2574 | const cacheLocation = getFixturePath(".eslintcache"); | |
2575 | ||
2576 | doDelete(cacheLocation); | |
2577 | ||
2578 | eslint = new ESLint({ | |
2579 | cwd: path.join(fixtureDir, ".."), | |
2580 | useEslintrc: false, | |
2581 | ||
2582 | // specifying cache true the cache will be created | |
2583 | cache: true, | |
2584 | cacheLocation, | |
2585 | overrideConfig: { | |
2586 | rules: { | |
2587 | "no-console": 0, | |
2588 | "no-unused-vars": 2 | |
2589 | } | |
2590 | }, | |
2591 | extensions: ["js"] | |
2592 | }); | |
2593 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2594 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2595 | const toBeDeletedFile = fs.realpathSync(getFixturePath("cache/src", "file-to-delete.js")); | |
2596 | ||
2597 | await eslint.lintFiles([badFile, goodFile, toBeDeletedFile]); | |
2598 | const fileCache = fCache.createFromFile(cacheLocation); | |
2599 | let { cache } = fileCache; | |
2600 | ||
2601 | assert.strictEqual(typeof cache.getKey(toBeDeletedFile), "object", "the entry for the file to be deleted is in the cache"); | |
2602 | ||
2603 | // delete the file from the file system | |
2604 | fs.unlinkSync(toBeDeletedFile); | |
2605 | ||
2606 | /* | |
2607 | * file-entry-cache@2.0.0 will remove from the cache deleted files | |
2608 | * even when they were not part of the array of files to be analyzed | |
2609 | */ | |
2610 | await eslint.lintFiles([badFile, goodFile]); | |
2611 | ||
2612 | cache = JSON.parse(fs.readFileSync(cacheLocation)); | |
2613 | ||
2614 | assert.strictEqual(typeof cache[toBeDeletedFile], "undefined", "the entry for the file to be deleted is not in the cache"); | |
2615 | }); | |
2616 | ||
2617 | it("should contain files that were not visited in the cache provided they still exist", async () => { | |
2618 | const cacheLocation = getFixturePath(".eslintcache"); | |
2619 | ||
2620 | doDelete(cacheLocation); | |
2621 | ||
2622 | eslint = new ESLint({ | |
2623 | cwd: path.join(fixtureDir, ".."), | |
2624 | useEslintrc: false, | |
2625 | ||
2626 | // specifying cache true the cache will be created | |
2627 | cache: true, | |
2628 | cacheLocation, | |
2629 | overrideConfig: { | |
2630 | rules: { | |
2631 | "no-console": 0, | |
2632 | "no-unused-vars": 2 | |
2633 | } | |
2634 | }, | |
2635 | extensions: ["js"] | |
2636 | }); | |
2637 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2638 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2639 | const testFile2 = fs.realpathSync(getFixturePath("cache/src", "test-file2.js")); | |
2640 | ||
2641 | await eslint.lintFiles([badFile, goodFile, testFile2]); | |
2642 | ||
2643 | let fileCache = fCache.createFromFile(cacheLocation); | |
2644 | let { cache } = fileCache; | |
2645 | ||
2646 | assert.strictEqual(typeof cache.getKey(testFile2), "object", "the entry for the test-file2 is in the cache"); | |
2647 | ||
2648 | /* | |
2649 | * we pass a different set of files minus test-file2 | |
2650 | * previous version of file-entry-cache would remove the non visited | |
2651 | * entries. 2.0.0 version will keep them unless they don't exist | |
2652 | */ | |
2653 | await eslint.lintFiles([badFile, goodFile]); | |
2654 | ||
2655 | fileCache = fCache.createFromFile(cacheLocation); | |
2656 | cache = fileCache.cache; | |
2657 | ||
2658 | assert.strictEqual(typeof cache.getKey(testFile2), "object", "the entry for the test-file2 is in the cache"); | |
2659 | }); | |
2660 | ||
2661 | it("should not delete cache when executing on text", async () => { | |
2662 | const cacheLocation = getFixturePath(".eslintcache"); | |
2663 | ||
2664 | eslint = new ESLint({ | |
2665 | cwd: path.join(fixtureDir, ".."), | |
2666 | useEslintrc: false, | |
2667 | cacheLocation, | |
2668 | overrideConfig: { | |
2669 | rules: { | |
2670 | "no-console": 0, | |
2671 | "no-unused-vars": 2 | |
2672 | } | |
2673 | }, | |
2674 | extensions: ["js"] | |
2675 | }); | |
2676 | ||
2677 | assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); | |
2678 | ||
2679 | await eslint.lintText("var foo = 'bar';"); | |
2680 | ||
2681 | assert(shell.test("-f", cacheLocation), "the cache for eslint still exists"); | |
2682 | }); | |
2683 | ||
2684 | it("should not delete cache when executing on text with a provided filename", async () => { | |
2685 | const cacheLocation = getFixturePath(".eslintcache"); | |
2686 | ||
2687 | eslint = new ESLint({ | |
2688 | cwd: path.join(fixtureDir, ".."), | |
2689 | useEslintrc: false, | |
2690 | cacheLocation, | |
2691 | overrideConfig: { | |
2692 | rules: { | |
2693 | "no-console": 0, | |
2694 | "no-unused-vars": 2 | |
2695 | } | |
2696 | }, | |
2697 | extensions: ["js"] | |
2698 | }); | |
2699 | ||
2700 | assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); | |
2701 | ||
2702 | await eslint.lintText("var bar = foo;", { filePath: "fixtures/passing.js" }); | |
2703 | ||
2704 | assert(shell.test("-f", cacheLocation), "the cache for eslint still exists"); | |
2705 | }); | |
2706 | ||
2707 | it("should not delete cache when executing on files with --cache flag", async () => { | |
2708 | const cacheLocation = getFixturePath(".eslintcache"); | |
2709 | ||
2710 | eslint = new ESLint({ | |
2711 | cwd: path.join(fixtureDir, ".."), | |
2712 | useEslintrc: false, | |
2713 | cache: true, | |
2714 | cacheLocation, | |
2715 | overrideConfig: { | |
2716 | rules: { | |
2717 | "no-console": 0, | |
2718 | "no-unused-vars": 2 | |
2719 | } | |
2720 | }, | |
2721 | extensions: ["js"] | |
2722 | }); | |
2723 | const file = getFixturePath("cli-engine", "console.js"); | |
2724 | ||
2725 | assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); | |
2726 | ||
2727 | await eslint.lintFiles([file]); | |
2728 | ||
2729 | assert(shell.test("-f", cacheLocation), "the cache for eslint still exists"); | |
2730 | }); | |
2731 | ||
2732 | it("should delete cache when executing on files without --cache flag", async () => { | |
2733 | const cacheLocation = getFixturePath(".eslintcache"); | |
2734 | ||
2735 | eslint = new ESLint({ | |
2736 | cwd: path.join(fixtureDir, ".."), | |
2737 | useEslintrc: false, | |
2738 | cacheLocation, | |
2739 | overrideConfig: { | |
2740 | rules: { | |
2741 | "no-console": 0, | |
2742 | "no-unused-vars": 2 | |
2743 | } | |
2744 | }, | |
2745 | extensions: ["js"] | |
2746 | }); | |
2747 | const file = getFixturePath("cli-engine", "console.js"); | |
2748 | ||
2749 | assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); | |
2750 | ||
2751 | await eslint.lintFiles([file]); | |
2752 | ||
2753 | assert(!shell.test("-f", cacheLocation), "the cache for eslint has been deleted"); | |
2754 | }); | |
2755 | ||
2756 | describe("cacheFile", () => { | |
2757 | it("should use the specified cache file", async () => { | |
2758 | const customCacheFile = path.resolve(".cache/custom-cache"); | |
2759 | ||
2760 | assert(!shell.test("-f", customCacheFile), "the cache for eslint does not exist"); | |
2761 | ||
2762 | eslint = new ESLint({ | |
2763 | useEslintrc: false, | |
2764 | ||
2765 | // specify a custom cache file | |
2766 | cacheLocation: customCacheFile, | |
2767 | ||
2768 | // specifying cache true the cache will be created | |
2769 | cache: true, | |
2770 | overrideConfig: { | |
2771 | rules: { | |
2772 | "no-console": 0, | |
2773 | "no-unused-vars": 2 | |
2774 | } | |
2775 | }, | |
2776 | extensions: ["js"], | |
2777 | cwd: path.join(fixtureDir, "..") | |
2778 | }); | |
2779 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2780 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2781 | const result = await eslint.lintFiles([badFile, goodFile]); | |
2782 | ||
2783 | assert(shell.test("-f", customCacheFile), "the cache for eslint was created"); | |
2784 | const fileCache = fCache.createFromFile(customCacheFile); | |
2785 | const { cache } = fileCache; | |
2786 | ||
2787 | assert(typeof cache.getKey(goodFile) === "object", "the entry for the good file is in the cache"); | |
2788 | ||
2789 | assert(typeof cache.getKey(badFile) === "object", "the entry for the bad file is in the cache"); | |
2790 | const cachedResult = await eslint.lintFiles([badFile, goodFile]); | |
2791 | ||
2792 | assert.deepStrictEqual(result, cachedResult, "result is the same with or without cache"); | |
2793 | }); | |
2794 | }); | |
5422a9cc TL |
2795 | |
2796 | describe("cacheStrategy", () => { | |
2797 | it("should detect changes using a file's modification time when set to 'metadata'", async () => { | |
2798 | const cacheLocation = getFixturePath(".eslintcache"); | |
2799 | ||
2800 | doDelete(cacheLocation); | |
2801 | ||
2802 | eslint = new ESLint({ | |
2803 | cwd: path.join(fixtureDir, ".."), | |
2804 | useEslintrc: false, | |
2805 | ||
2806 | // specifying cache true the cache will be created | |
2807 | cache: true, | |
2808 | cacheLocation, | |
2809 | cacheStrategy: "metadata", | |
2810 | overrideConfig: { | |
2811 | rules: { | |
2812 | "no-console": 0, | |
2813 | "no-unused-vars": 2 | |
2814 | } | |
2815 | }, | |
2816 | extensions: ["js"] | |
2817 | }); | |
2818 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2819 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2820 | ||
2821 | await eslint.lintFiles([badFile, goodFile]); | |
2822 | let fileCache = fCache.createFromFile(cacheLocation); | |
2823 | const entries = fileCache.normalizeEntries([badFile, goodFile]); | |
2824 | ||
2825 | entries.forEach(entry => { | |
2826 | assert(entry.changed === false, `the entry for ${entry.key} is initially unchanged`); | |
2827 | }); | |
2828 | ||
2829 | // this should result in a changed entry | |
2830 | shell.touch(goodFile); | |
2831 | fileCache = fCache.createFromFile(cacheLocation); | |
2832 | assert(fileCache.getFileDescriptor(badFile).changed === false, `the entry for ${badFile} is unchanged`); | |
2833 | assert(fileCache.getFileDescriptor(goodFile).changed === true, `the entry for ${goodFile} is changed`); | |
2834 | }); | |
2835 | ||
2836 | it("should not detect changes using a file's modification time when set to 'content'", async () => { | |
2837 | const cacheLocation = getFixturePath(".eslintcache"); | |
2838 | ||
2839 | doDelete(cacheLocation); | |
2840 | ||
2841 | eslint = new ESLint({ | |
2842 | cwd: path.join(fixtureDir, ".."), | |
2843 | useEslintrc: false, | |
2844 | ||
2845 | // specifying cache true the cache will be created | |
2846 | cache: true, | |
2847 | cacheLocation, | |
2848 | cacheStrategy: "content", | |
2849 | overrideConfig: { | |
2850 | rules: { | |
2851 | "no-console": 0, | |
2852 | "no-unused-vars": 2 | |
2853 | } | |
2854 | }, | |
2855 | extensions: ["js"] | |
2856 | }); | |
2857 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2858 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2859 | ||
2860 | await eslint.lintFiles([badFile, goodFile]); | |
2861 | let fileCache = fCache.createFromFile(cacheLocation, true); | |
2862 | let entries = fileCache.normalizeEntries([badFile, goodFile]); | |
2863 | ||
2864 | entries.forEach(entry => { | |
2865 | assert(entry.changed === false, `the entry for ${entry.key} is initially unchanged`); | |
2866 | }); | |
2867 | ||
2868 | // this should NOT result in a changed entry | |
2869 | shell.touch(goodFile); | |
2870 | fileCache = fCache.createFromFile(cacheLocation, true); | |
2871 | entries = fileCache.normalizeEntries([badFile, goodFile]); | |
2872 | entries.forEach(entry => { | |
2873 | assert(entry.changed === false, `the entry for ${entry.key} remains unchanged`); | |
2874 | }); | |
2875 | }); | |
2876 | ||
2877 | it("should detect changes using a file's contents when set to 'content'", async () => { | |
2878 | const cacheLocation = getFixturePath(".eslintcache"); | |
2879 | ||
2880 | doDelete(cacheLocation); | |
2881 | ||
2882 | eslint = new ESLint({ | |
2883 | cwd: path.join(fixtureDir, ".."), | |
2884 | useEslintrc: false, | |
2885 | ||
2886 | // specifying cache true the cache will be created | |
2887 | cache: true, | |
2888 | cacheLocation, | |
2889 | cacheStrategy: "content", | |
2890 | overrideConfig: { | |
2891 | rules: { | |
2892 | "no-console": 0, | |
2893 | "no-unused-vars": 2 | |
2894 | } | |
2895 | }, | |
2896 | extensions: ["js"] | |
2897 | }); | |
2898 | const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); | |
2899 | const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); | |
2900 | const goodFileCopy = path.resolve(`${path.dirname(goodFile)}`, "test-file-copy.js"); | |
2901 | ||
2902 | shell.cp(goodFile, goodFileCopy); | |
2903 | ||
2904 | await eslint.lintFiles([badFile, goodFileCopy]); | |
2905 | let fileCache = fCache.createFromFile(cacheLocation, true); | |
2906 | const entries = fileCache.normalizeEntries([badFile, goodFileCopy]); | |
2907 | ||
2908 | entries.forEach(entry => { | |
2909 | assert(entry.changed === false, `the entry for ${entry.key} is initially unchanged`); | |
2910 | }); | |
2911 | ||
2912 | // this should result in a changed entry | |
2913 | shell.sed("-i", "abc", "xzy", goodFileCopy); | |
2914 | fileCache = fCache.createFromFile(cacheLocation, true); | |
2915 | assert(fileCache.getFileDescriptor(badFile).changed === false, `the entry for ${badFile} is unchanged`); | |
2916 | assert(fileCache.getFileDescriptor(goodFileCopy).changed === true, `the entry for ${goodFileCopy} is changed`); | |
2917 | }); | |
2918 | }); | |
56c4a2cb DC |
2919 | }); |
2920 | ||
2921 | describe("processors", () => { | |
2922 | it("should return two messages when executing with config file that specifies a processor", async () => { | |
2923 | eslint = eslintWithPlugins({ | |
2924 | overrideConfigFile: getFixturePath("configurations", "processors.json"), | |
2925 | useEslintrc: false, | |
2926 | extensions: ["js", "txt"], | |
2927 | cwd: path.join(fixtureDir, "..") | |
2928 | }); | |
2929 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("processors", "test", "test-processor.txt"))]); | |
2930 | ||
2931 | assert.strictEqual(results.length, 1); | |
2932 | assert.strictEqual(results[0].messages.length, 2); | |
2933 | }); | |
2934 | ||
2935 | it("should return two messages when executing with config file that specifies preloaded processor", async () => { | |
2936 | eslint = new ESLint({ | |
2937 | useEslintrc: false, | |
2938 | overrideConfig: { | |
2939 | plugins: ["test-processor"], | |
2940 | rules: { | |
2941 | "no-console": 2, | |
2942 | "no-unused-vars": 2 | |
2943 | } | |
2944 | }, | |
2945 | extensions: ["js", "txt"], | |
2946 | cwd: path.join(fixtureDir, ".."), | |
2947 | plugins: { | |
2948 | "test-processor": { | |
2949 | processors: { | |
2950 | ".txt": { | |
2951 | preprocess(text) { | |
2952 | return [text]; | |
2953 | }, | |
2954 | postprocess(messages) { | |
2955 | return messages[0]; | |
2956 | } | |
2957 | } | |
2958 | } | |
2959 | } | |
2960 | } | |
2961 | }); | |
2962 | const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("processors", "test", "test-processor.txt"))]); | |
2963 | ||
2964 | assert.strictEqual(results.length, 1); | |
2965 | assert.strictEqual(results[0].messages.length, 2); | |
2966 | }); | |
2967 | ||
2968 | it("should run processors when calling lintFiles with config file that specifies a processor", async () => { | |
2969 | eslint = eslintWithPlugins({ | |
2970 | overrideConfigFile: getFixturePath("configurations", "processors.json"), | |
2971 | useEslintrc: false, | |
2972 | extensions: ["js", "txt"], | |
2973 | cwd: path.join(fixtureDir, "..") | |
2974 | }); | |
2975 | const results = await eslint.lintFiles([getFixturePath("processors", "test", "test-processor.txt")]); | |
2976 | ||
2977 | assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); | |
2978 | assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); | |
2979 | }); | |
2980 | ||
2981 | it("should run processors when calling lintFiles with config file that specifies preloaded processor", async () => { | |
2982 | eslint = new ESLint({ | |
2983 | useEslintrc: false, | |
2984 | overrideConfig: { | |
2985 | plugins: ["test-processor"], | |
2986 | rules: { | |
2987 | "no-console": 2, | |
2988 | "no-unused-vars": 2 | |
2989 | } | |
2990 | }, | |
2991 | extensions: ["js", "txt"], | |
2992 | cwd: path.join(fixtureDir, ".."), | |
2993 | plugins: { | |
2994 | "test-processor": { | |
2995 | processors: { | |
2996 | ".txt": { | |
2997 | preprocess(text) { | |
2998 | return [text.replace("a()", "b()")]; | |
2999 | }, | |
3000 | postprocess(messages) { | |
3001 | messages[0][0].ruleId = "post-processed"; | |
3002 | return messages[0]; | |
3003 | } | |
3004 | } | |
3005 | } | |
3006 | } | |
3007 | } | |
3008 | }); | |
3009 | const results = await eslint.lintFiles([getFixturePath("processors", "test", "test-processor.txt")]); | |
3010 | ||
3011 | assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); | |
3012 | assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); | |
3013 | }); | |
3014 | ||
3015 | it("should run processors when calling lintText with config file that specifies a processor", async () => { | |
3016 | eslint = eslintWithPlugins({ | |
3017 | overrideConfigFile: getFixturePath("configurations", "processors.json"), | |
3018 | useEslintrc: false, | |
3019 | extensions: ["js", "txt"], | |
3020 | ignore: false | |
3021 | }); | |
3022 | const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); | |
3023 | ||
3024 | assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); | |
3025 | assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); | |
3026 | }); | |
3027 | ||
3028 | it("should run processors when calling lintText with config file that specifies preloaded processor", async () => { | |
3029 | eslint = new ESLint({ | |
3030 | useEslintrc: false, | |
3031 | overrideConfig: { | |
3032 | plugins: ["test-processor"], | |
3033 | rules: { | |
3034 | "no-console": 2, | |
3035 | "no-unused-vars": 2 | |
3036 | } | |
3037 | }, | |
3038 | extensions: ["js", "txt"], | |
3039 | ignore: false, | |
3040 | plugins: { | |
3041 | "test-processor": { | |
3042 | processors: { | |
3043 | ".txt": { | |
3044 | preprocess(text) { | |
3045 | return [text.replace("a()", "b()")]; | |
3046 | }, | |
3047 | postprocess(messages) { | |
3048 | messages[0][0].ruleId = "post-processed"; | |
3049 | return messages[0]; | |
3050 | } | |
3051 | } | |
3052 | } | |
3053 | } | |
3054 | } | |
3055 | }); | |
3056 | const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); | |
3057 | ||
3058 | assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); | |
3059 | assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); | |
3060 | }); | |
3061 | ||
5422a9cc TL |
3062 | it("should run processors when calling lintText with processor resolves same extension but different content correctly", async () => { |
3063 | let count = 0; | |
3064 | ||
3065 | eslint = new ESLint({ | |
3066 | useEslintrc: false, | |
3067 | overrideConfig: { | |
3068 | plugins: ["test-processor"], | |
3069 | overrides: [{ | |
3070 | files: ["**/*.txt/*.txt"], | |
3071 | rules: { | |
3072 | "no-console": 2, | |
3073 | "no-unused-vars": 2 | |
3074 | } | |
3075 | }] | |
3076 | }, | |
3077 | extensions: ["txt"], | |
3078 | ignore: false, | |
3079 | plugins: { | |
3080 | "test-processor": { | |
3081 | processors: { | |
3082 | ".txt": { | |
3083 | preprocess(text) { | |
3084 | count++; | |
3085 | return [ | |
3086 | { | |
3087 | ||
3088 | // it will be run twice, and text will be as-is at the second time, then it will not run third time | |
3089 | text: text.replace("a()", "b()"), | |
3090 | filename: ".txt" | |
3091 | } | |
3092 | ]; | |
3093 | }, | |
3094 | postprocess(messages) { | |
3095 | messages[0][0].ruleId = "post-processed"; | |
3096 | return messages[0]; | |
3097 | } | |
3098 | } | |
3099 | } | |
3100 | } | |
3101 | } | |
3102 | }); | |
3103 | const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); | |
3104 | ||
3105 | assert.strictEqual(count, 2); | |
3106 | assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); | |
3107 | assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); | |
3108 | }); | |
3109 | ||
56c4a2cb DC |
3110 | describe("autofixing with processors", () => { |
3111 | const HTML_PROCESSOR = Object.freeze({ | |
3112 | preprocess(text) { | |
3113 | return [text.replace(/^<script>/u, "").replace(/<\/script>$/u, "")]; | |
3114 | }, | |
3115 | postprocess(problemLists) { | |
3116 | return problemLists[0].map(problem => { | |
3117 | if (problem.fix) { | |
3118 | const updatedFix = Object.assign({}, problem.fix, { | |
3119 | range: problem.fix.range.map(index => index + "<script>".length) | |
3120 | }); | |
3121 | ||
3122 | return Object.assign({}, problem, { fix: updatedFix }); | |
3123 | } | |
3124 | return problem; | |
3125 | }); | |
3126 | } | |
3127 | }); | |
3128 | ||
3129 | ||
3130 | it("should run in autofix mode when using a processor that supports autofixing", async () => { | |
3131 | eslint = new ESLint({ | |
3132 | useEslintrc: false, | |
3133 | overrideConfig: { | |
3134 | plugins: ["test-processor"], | |
3135 | rules: { | |
3136 | semi: 2 | |
3137 | } | |
3138 | }, | |
3139 | extensions: ["js", "txt"], | |
3140 | ignore: false, | |
3141 | fix: true, | |
3142 | plugins: { | |
3143 | "test-processor": { | |
3144 | processors: { | |
3145 | ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) | |
3146 | } | |
3147 | } | |
3148 | } | |
3149 | }); | |
3150 | const results = await eslint.lintText("<script>foo</script>", { filePath: "foo.html" }); | |
3151 | ||
3152 | assert.strictEqual(results[0].messages.length, 0); | |
3153 | assert.strictEqual(results[0].output, "<script>foo;</script>"); | |
3154 | }); | |
3155 | ||
3156 | it("should not run in autofix mode when using a processor that does not support autofixing", async () => { | |
3157 | eslint = new ESLint({ | |
3158 | useEslintrc: false, | |
3159 | overrideConfig: { | |
3160 | plugins: ["test-processor"], | |
3161 | rules: { | |
3162 | semi: 2 | |
3163 | } | |
3164 | }, | |
3165 | extensions: ["js", "txt"], | |
3166 | ignore: false, | |
3167 | fix: true, | |
3168 | plugins: { | |
3169 | "test-processor": { processors: { ".html": HTML_PROCESSOR } } | |
3170 | } | |
3171 | }); | |
3172 | const results = await eslint.lintText("<script>foo</script>", { filePath: "foo.html" }); | |
3173 | ||
3174 | assert.strictEqual(results[0].messages.length, 1); | |
3175 | assert(!Object.prototype.hasOwnProperty.call(results[0], "output")); | |
3176 | }); | |
3177 | ||
3178 | it("should not run in autofix mode when `fix: true` is not provided, even if the processor supports autofixing", async () => { | |
3179 | eslint = new ESLint({ | |
3180 | useEslintrc: false, | |
3181 | overrideConfig: { | |
3182 | plugins: ["test-processor"], | |
3183 | rules: { | |
3184 | semi: 2 | |
3185 | } | |
3186 | }, | |
3187 | extensions: ["js", "txt"], | |
3188 | ignore: false, | |
3189 | plugins: { | |
3190 | "test-processor": { | |
3191 | processors: { | |
3192 | ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) | |
3193 | } | |
3194 | } | |
3195 | } | |
3196 | }); | |
3197 | const results = await eslint.lintText("<script>foo</script>", { filePath: "foo.html" }); | |
3198 | ||
3199 | assert.strictEqual(results[0].messages.length, 1); | |
3200 | assert(!Object.prototype.hasOwnProperty.call(results[0], "output")); | |
3201 | }); | |
3202 | }); | |
3203 | }); | |
3204 | ||
3205 | describe("Patterns which match no file should throw errors.", () => { | |
3206 | beforeEach(() => { | |
3207 | eslint = new ESLint({ | |
3208 | cwd: getFixturePath("cli-engine"), | |
3209 | useEslintrc: false | |
3210 | }); | |
3211 | }); | |
3212 | ||
3213 | it("one file", async () => { | |
3214 | await assert.rejects(async () => { | |
3215 | await eslint.lintFiles(["non-exist.js"]); | |
3216 | }, /No files matching 'non-exist\.js' were found\./u); | |
3217 | }); | |
3218 | ||
3219 | it("should throw if the directory exists and is empty", async () => { | |
3220 | await assert.rejects(async () => { | |
3221 | await eslint.lintFiles(["empty"]); | |
3222 | }, /No files matching 'empty' were found\./u); | |
3223 | }); | |
3224 | ||
3225 | it("one glob pattern", async () => { | |
3226 | await assert.rejects(async () => { | |
3227 | await eslint.lintFiles(["non-exist/**/*.js"]); | |
3228 | }, /No files matching 'non-exist\/\*\*\/\*\.js' were found\./u); | |
3229 | }); | |
3230 | ||
3231 | it("two files", async () => { | |
3232 | await assert.rejects(async () => { | |
3233 | await eslint.lintFiles(["aaa.js", "bbb.js"]); | |
3234 | }, /No files matching 'aaa\.js' were found\./u); | |
3235 | }); | |
3236 | ||
3237 | it("a mix of an existing file and a non-existing file", async () => { | |
3238 | await assert.rejects(async () => { | |
3239 | await eslint.lintFiles(["console.js", "non-exist.js"]); | |
3240 | }, /No files matching 'non-exist\.js' were found\./u); | |
3241 | }); | |
3242 | }); | |
3243 | ||
3244 | describe("overrides", () => { | |
3245 | beforeEach(() => { | |
3246 | eslint = new ESLint({ | |
3247 | cwd: getFixturePath("cli-engine/overrides-with-dot"), | |
3248 | ignore: false | |
3249 | }); | |
3250 | }); | |
3251 | ||
3252 | it("should recognize dotfiles", async () => { | |
3253 | const ret = await eslint.lintFiles([".test-target.js"]); | |
3254 | ||
3255 | assert.strictEqual(ret.length, 1); | |
3256 | assert.strictEqual(ret[0].messages.length, 1); | |
3257 | assert.strictEqual(ret[0].messages[0].ruleId, "no-unused-vars"); | |
3258 | }); | |
3259 | }); | |
3260 | ||
3261 | describe("a config file setting should have higher priority than a shareable config file's settings always; https://github.com/eslint/eslint/issues/11510", () => { | |
6f036462 TL |
3262 | |
3263 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
3264 | cwd: path.join(os.tmpdir(), "eslint/11510"), | |
3265 | files: { | |
3266 | "no-console-error-in-overrides.json": JSON.stringify({ | |
3267 | overrides: [{ | |
3268 | files: ["*.js"], | |
3269 | rules: { "no-console": "error" } | |
3270 | }] | |
3271 | }), | |
3272 | ".eslintrc.json": JSON.stringify({ | |
3273 | extends: "./no-console-error-in-overrides.json", | |
3274 | rules: { "no-console": "off" } | |
3275 | }), | |
3276 | "a.js": "console.log();" | |
3277 | } | |
3278 | }); | |
3279 | ||
56c4a2cb | 3280 | beforeEach(() => { |
6f036462 TL |
3281 | eslint = new ESLint({ cwd: getPath() }); |
3282 | return prepare(); | |
56c4a2cb DC |
3283 | }); |
3284 | ||
6f036462 TL |
3285 | afterEach(cleanup); |
3286 | ||
56c4a2cb DC |
3287 | it("should not report 'no-console' error.", async () => { |
3288 | const results = await eslint.lintFiles("a.js"); | |
3289 | ||
3290 | assert.strictEqual(results.length, 1); | |
3291 | assert.deepStrictEqual(results[0].messages, []); | |
3292 | }); | |
3293 | }); | |
3294 | ||
3295 | describe("configs of plugin rules should be validated even if 'plugins' key doesn't exist; https://github.com/eslint/eslint/issues/11559", () => { | |
6f036462 TL |
3296 | |
3297 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
3298 | cwd: path.join(os.tmpdir(), "eslint/11559"), | |
3299 | files: { | |
3300 | "node_modules/eslint-plugin-test/index.js": ` | |
56c4a2cb DC |
3301 | exports.configs = { |
3302 | recommended: { plugins: ["test"] } | |
3303 | }; | |
3304 | exports.rules = { | |
3305 | foo: { | |
3306 | meta: { schema: [{ type: "number" }] }, | |
3307 | create() { return {}; } | |
3308 | } | |
3309 | }; | |
3310 | `, | |
6f036462 | 3311 | ".eslintrc.json": JSON.stringify({ |
56c4a2cb | 3312 | |
6f036462 TL |
3313 | // Import via the recommended config. |
3314 | extends: "plugin:test/recommended", | |
56c4a2cb | 3315 | |
6f036462 TL |
3316 | // Has invalid option. |
3317 | rules: { "test/foo": ["error", "invalid-option"] } | |
3318 | }), | |
3319 | "a.js": "console.log();" | |
3320 | } | |
56c4a2cb DC |
3321 | }); |
3322 | ||
6f036462 TL |
3323 | beforeEach(() => { |
3324 | eslint = new ESLint({ cwd: getPath() }); | |
3325 | return prepare(); | |
3326 | }); | |
3327 | ||
3328 | afterEach(cleanup); | |
3329 | ||
3330 | ||
56c4a2cb DC |
3331 | it("should throw fatal error.", async () => { |
3332 | await assert.rejects(async () => { | |
3333 | await eslint.lintFiles("a.js"); | |
3334 | }, /invalid-option/u); | |
3335 | }); | |
3336 | }); | |
3337 | ||
3338 | describe("'--fix-type' should not crash even if plugin rules exist; https://github.com/eslint/eslint/issues/11586", () => { | |
6f036462 TL |
3339 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
3340 | cwd: path.join(os.tmpdir(), "cli-engine/11586"), | |
3341 | files: { | |
3342 | "node_modules/eslint-plugin-test/index.js": ` | |
56c4a2cb DC |
3343 | exports.rules = { |
3344 | "no-example": { | |
3345 | meta: { type: "problem", fixable: "code" }, | |
3346 | create(context) { | |
3347 | return { | |
3348 | Identifier(node) { | |
3349 | if (node.name === "example") { | |
3350 | context.report({ | |
3351 | node, | |
3352 | message: "fix", | |
3353 | fix: fixer => fixer.replaceText(node, "fixed") | |
3354 | }) | |
3355 | } | |
3356 | } | |
3357 | }; | |
3358 | } | |
3359 | } | |
3360 | }; | |
3361 | `, | |
6f036462 TL |
3362 | ".eslintrc.json": { |
3363 | plugins: ["test"], | |
3364 | rules: { "test/no-example": "error" } | |
3365 | }, | |
3366 | "a.js": "example;" | |
3367 | } | |
56c4a2cb DC |
3368 | }); |
3369 | ||
6f036462 TL |
3370 | beforeEach(() => { |
3371 | eslint = new ESLint({ | |
3372 | cwd: getPath(), | |
3373 | fix: true, | |
3374 | fixTypes: ["problem"] | |
3375 | }); | |
3376 | ||
3377 | return prepare(); | |
3378 | }); | |
3379 | ||
3380 | afterEach(cleanup); | |
3381 | ||
56c4a2cb DC |
3382 | it("should not crash.", async () => { |
3383 | const results = await eslint.lintFiles("a.js"); | |
3384 | ||
3385 | assert.strictEqual(results.length, 1); | |
3386 | assert.deepStrictEqual(results[0].messages, []); | |
3387 | assert.deepStrictEqual(results[0].output, "fixed;"); | |
3388 | }); | |
3389 | }); | |
3390 | ||
3391 | describe("multiple processors", () => { | |
3392 | const root = path.join(os.tmpdir(), "eslint/eslint/multiple-processors"); | |
3393 | const commonFiles = { | |
3394 | "node_modules/pattern-processor/index.js": fs.readFileSync( | |
3395 | require.resolve("../../fixtures/processors/pattern-processor"), | |
3396 | "utf8" | |
3397 | ), | |
3398 | "node_modules/eslint-plugin-markdown/index.js": ` | |
3399 | const { defineProcessor } = require("pattern-processor"); | |
3400 | const processor = defineProcessor(${/```(\w+)\n([\s\S]+?)\n```/gu}); | |
3401 | exports.processors = { | |
3402 | ".md": { ...processor, supportsAutofix: true }, | |
3403 | "non-fixable": processor | |
3404 | }; | |
3405 | `, | |
3406 | "node_modules/eslint-plugin-html/index.js": ` | |
3407 | const { defineProcessor } = require("pattern-processor"); | |
3408 | const processor = defineProcessor(${/<script lang="(\w*)">\n([\s\S]+?)\n<\/script>/gu}); | |
3409 | const legacyProcessor = defineProcessor(${/<script lang="(\w*)">\n([\s\S]+?)\n<\/script>/gu}, true); | |
3410 | exports.processors = { | |
3411 | ".html": { ...processor, supportsAutofix: true }, | |
3412 | "non-fixable": processor, | |
3413 | "legacy": legacyProcessor | |
3414 | }; | |
3415 | `, | |
3416 | "test.md": unIndent` | |
3417 | \`\`\`js | |
3418 | console.log("hello") | |
3419 | \`\`\` | |
3420 | \`\`\`html | |
3421 | <div>Hello</div> | |
3422 | <script lang="js"> | |
3423 | console.log("hello") | |
3424 | </script> | |
3425 | <script lang="ts"> | |
3426 | console.log("hello") | |
3427 | </script> | |
3428 | \`\`\` | |
3429 | ` | |
3430 | }; | |
3431 | ||
6f036462 TL |
3432 | let cleanup; |
3433 | ||
3434 | beforeEach(() => { | |
3435 | cleanup = () => { }; | |
3436 | }); | |
3437 | ||
3438 | afterEach(() => cleanup()); | |
3439 | ||
56c4a2cb | 3440 | it("should lint only JavaScript blocks if '--ext' was not given.", async () => { |
6f036462 TL |
3441 | const teardown = createCustomTeardown({ |
3442 | cwd: root, | |
56c4a2cb DC |
3443 | files: { |
3444 | ...commonFiles, | |
6f036462 | 3445 | ".eslintrc.json": { |
56c4a2cb DC |
3446 | plugins: ["markdown", "html"], |
3447 | rules: { semi: "error" } | |
6f036462 | 3448 | } |
56c4a2cb | 3449 | } |
6f036462 TL |
3450 | }); |
3451 | ||
3452 | cleanup = teardown.cleanup; | |
3453 | await teardown.prepare(); | |
3454 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
3455 | const results = await eslint.lintFiles(["test.md"]); |
3456 | ||
3457 | assert.strictEqual(results.length, 1); | |
3458 | assert.strictEqual(results[0].messages.length, 1); | |
3459 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); | |
3460 | assert.strictEqual(results[0].messages[0].line, 2); | |
3461 | }); | |
3462 | ||
3463 | it("should fix only JavaScript blocks if '--ext' was not given.", async () => { | |
6f036462 TL |
3464 | const teardown = createCustomTeardown({ |
3465 | cwd: root, | |
56c4a2cb DC |
3466 | files: { |
3467 | ...commonFiles, | |
6f036462 | 3468 | ".eslintrc.json": { |
56c4a2cb DC |
3469 | plugins: ["markdown", "html"], |
3470 | rules: { semi: "error" } | |
6f036462 | 3471 | } |
56c4a2cb | 3472 | } |
6f036462 TL |
3473 | }); |
3474 | ||
3475 | await teardown.prepare(); | |
3476 | cleanup = teardown.cleanup; | |
3477 | eslint = new ESLint({ cwd: teardown.getPath(), fix: true }); | |
56c4a2cb DC |
3478 | const results = await eslint.lintFiles(["test.md"]); |
3479 | ||
3480 | assert.strictEqual(results.length, 1); | |
3481 | assert.strictEqual(results[0].messages.length, 0); | |
3482 | assert.strictEqual(results[0].output, unIndent` | |
3483 | \`\`\`js | |
3484 | console.log("hello");${/* ← fixed */""} | |
3485 | \`\`\` | |
3486 | \`\`\`html | |
3487 | <div>Hello</div> | |
3488 | <script lang="js"> | |
3489 | console.log("hello")${/* ← ignored */""} | |
3490 | </script> | |
3491 | <script lang="ts"> | |
3492 | console.log("hello")${/* ← ignored */""} | |
3493 | </script> | |
3494 | \`\`\` | |
3495 | `); | |
3496 | }); | |
3497 | ||
3498 | it("should lint HTML blocks as well with multiple processors if '--ext' option was given.", async () => { | |
6f036462 TL |
3499 | const teardown = createCustomTeardown({ |
3500 | cwd: root, | |
56c4a2cb DC |
3501 | files: { |
3502 | ...commonFiles, | |
6f036462 | 3503 | ".eslintrc.json": { |
56c4a2cb DC |
3504 | plugins: ["markdown", "html"], |
3505 | rules: { semi: "error" } | |
6f036462 | 3506 | } |
56c4a2cb | 3507 | } |
6f036462 TL |
3508 | }); |
3509 | ||
3510 | await teardown.prepare(); | |
3511 | cleanup = teardown.cleanup; | |
3512 | eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); | |
56c4a2cb DC |
3513 | const results = await eslint.lintFiles(["test.md"]); |
3514 | ||
3515 | assert.strictEqual(results.length, 1); | |
3516 | assert.strictEqual(results[0].messages.length, 2); | |
3517 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); // JS block | |
3518 | assert.strictEqual(results[0].messages[0].line, 2); | |
3519 | assert.strictEqual(results[0].messages[1].ruleId, "semi"); // JS block in HTML block | |
3520 | assert.strictEqual(results[0].messages[1].line, 7); | |
3521 | }); | |
3522 | ||
3523 | it("should fix HTML blocks as well with multiple processors if '--ext' option was given.", async () => { | |
6f036462 TL |
3524 | const teardown = createCustomTeardown({ |
3525 | cwd: root, | |
56c4a2cb DC |
3526 | files: { |
3527 | ...commonFiles, | |
6f036462 | 3528 | ".eslintrc.json": { |
56c4a2cb DC |
3529 | plugins: ["markdown", "html"], |
3530 | rules: { semi: "error" } | |
6f036462 | 3531 | } |
56c4a2cb | 3532 | } |
6f036462 TL |
3533 | }); |
3534 | ||
3535 | await teardown.prepare(); | |
3536 | cleanup = teardown.cleanup; | |
3537 | eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"], fix: true }); | |
56c4a2cb DC |
3538 | const results = await eslint.lintFiles(["test.md"]); |
3539 | ||
3540 | assert.strictEqual(results.length, 1); | |
3541 | assert.strictEqual(results[0].messages.length, 0); | |
3542 | assert.strictEqual(results[0].output, unIndent` | |
3543 | \`\`\`js | |
3544 | console.log("hello");${/* ← fixed */""} | |
3545 | \`\`\` | |
3546 | \`\`\`html | |
3547 | <div>Hello</div> | |
3548 | <script lang="js"> | |
3549 | console.log("hello");${/* ← fixed */""} | |
3550 | </script> | |
3551 | <script lang="ts"> | |
3552 | console.log("hello")${/* ← ignored */""} | |
3553 | </script> | |
3554 | \`\`\` | |
3555 | `); | |
3556 | }); | |
3557 | ||
6f036462 TL |
3558 | it("should use overridden processor; should report HTML blocks but not fix HTML blocks if the processor for '*.html' didn't support autofix.", async () => { |
3559 | const teardown = createCustomTeardown({ | |
3560 | cwd: root, | |
56c4a2cb DC |
3561 | files: { |
3562 | ...commonFiles, | |
6f036462 | 3563 | ".eslintrc.json": { |
56c4a2cb DC |
3564 | plugins: ["markdown", "html"], |
3565 | rules: { semi: "error" }, | |
3566 | overrides: [ | |
3567 | { | |
3568 | files: "*.html", | |
3569 | processor: "html/non-fixable" // supportsAutofix: false | |
3570 | } | |
3571 | ] | |
6f036462 | 3572 | } |
56c4a2cb | 3573 | } |
6f036462 TL |
3574 | }); |
3575 | ||
3576 | await teardown.prepare(); | |
3577 | cleanup = teardown.cleanup; | |
3578 | eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"], fix: true }); | |
56c4a2cb DC |
3579 | const results = await eslint.lintFiles(["test.md"]); |
3580 | ||
3581 | assert.strictEqual(results.length, 1); | |
3582 | assert.strictEqual(results[0].messages.length, 1); | |
3583 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); // JS Block in HTML Block | |
3584 | assert.strictEqual(results[0].messages[0].line, 7); | |
3585 | assert.strictEqual(results[0].messages[0].fix, void 0); | |
3586 | assert.strictEqual(results[0].output, unIndent` | |
3587 | \`\`\`js | |
3588 | console.log("hello");${/* ← fixed */""} | |
3589 | \`\`\` | |
3590 | \`\`\`html | |
3591 | <div>Hello</div> | |
3592 | <script lang="js"> | |
3593 | console.log("hello")${/* ← reported but not fixed */""} | |
3594 | </script> | |
3595 | <script lang="ts"> | |
3596 | console.log("hello") | |
3597 | </script> | |
3598 | \`\`\` | |
3599 | `); | |
3600 | }); | |
3601 | ||
3602 | it("should use the config '**/*.html/*.js' to lint JavaScript blocks in HTML.", async () => { | |
6f036462 TL |
3603 | const teardown = createCustomTeardown({ |
3604 | cwd: root, | |
56c4a2cb DC |
3605 | files: { |
3606 | ...commonFiles, | |
6f036462 | 3607 | ".eslintrc.json": { |
56c4a2cb DC |
3608 | plugins: ["markdown", "html"], |
3609 | rules: { semi: "error" }, | |
3610 | overrides: [ | |
3611 | { | |
3612 | files: "*.html", | |
3613 | ||
3614 | // this rules are not used because ESLint re-resolve configs if a code block had a different file extension. | |
3615 | rules: { | |
3616 | semi: "error", | |
3617 | "no-console": "off" | |
3618 | } | |
3619 | }, | |
3620 | { | |
3621 | files: "**/*.html/*.js", | |
3622 | rules: { | |
3623 | semi: "off", | |
3624 | "no-console": "error" | |
3625 | } | |
3626 | } | |
3627 | ] | |
6f036462 | 3628 | } |
56c4a2cb | 3629 | } |
6f036462 TL |
3630 | }); |
3631 | ||
3632 | await teardown.prepare(); | |
3633 | cleanup = teardown.cleanup; | |
3634 | eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); | |
56c4a2cb DC |
3635 | const results = await eslint.lintFiles(["test.md"]); |
3636 | ||
3637 | assert.strictEqual(results.length, 1); | |
3638 | assert.strictEqual(results[0].messages.length, 2); | |
3639 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); | |
3640 | assert.strictEqual(results[0].messages[0].line, 2); | |
3641 | assert.strictEqual(results[0].messages[1].ruleId, "no-console"); | |
3642 | assert.strictEqual(results[0].messages[1].line, 7); | |
3643 | }); | |
3644 | ||
3645 | it("should use the same config as one which has 'processor' property in order to lint blocks in HTML if the processor was legacy style.", async () => { | |
6f036462 TL |
3646 | const teardown = createCustomTeardown({ |
3647 | cwd: root, | |
56c4a2cb DC |
3648 | files: { |
3649 | ...commonFiles, | |
6f036462 | 3650 | ".eslintrc.json": { |
56c4a2cb DC |
3651 | plugins: ["markdown", "html"], |
3652 | rules: { semi: "error" }, | |
3653 | overrides: [ | |
3654 | { | |
3655 | files: "*.html", | |
3656 | processor: "html/legacy", // this processor returns strings rather than `{text, filename}` | |
3657 | rules: { | |
3658 | semi: "off", | |
3659 | "no-console": "error" | |
3660 | } | |
3661 | }, | |
3662 | { | |
3663 | files: "**/*.html/*.js", | |
3664 | rules: { | |
3665 | semi: "error", | |
3666 | "no-console": "off" | |
3667 | } | |
3668 | } | |
3669 | ] | |
6f036462 | 3670 | } |
56c4a2cb | 3671 | } |
6f036462 TL |
3672 | }); |
3673 | ||
3674 | await teardown.prepare(); | |
3675 | cleanup = teardown.cleanup; | |
3676 | eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); | |
56c4a2cb DC |
3677 | const results = await eslint.lintFiles(["test.md"]); |
3678 | ||
3679 | assert.strictEqual(results.length, 1); | |
3680 | assert.strictEqual(results[0].messages.length, 3); | |
3681 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); | |
3682 | assert.strictEqual(results[0].messages[0].line, 2); | |
3683 | assert.strictEqual(results[0].messages[1].ruleId, "no-console"); | |
3684 | assert.strictEqual(results[0].messages[1].line, 7); | |
3685 | assert.strictEqual(results[0].messages[2].ruleId, "no-console"); | |
3686 | assert.strictEqual(results[0].messages[2].line, 10); | |
3687 | }); | |
3688 | ||
3689 | it("should throw an error if invalid processor was specified.", async () => { | |
6f036462 TL |
3690 | const teardown = createCustomTeardown({ |
3691 | cwd: root, | |
56c4a2cb DC |
3692 | files: { |
3693 | ...commonFiles, | |
6f036462 | 3694 | ".eslintrc.json": { |
56c4a2cb DC |
3695 | plugins: ["markdown", "html"], |
3696 | processor: "markdown/unknown" | |
6f036462 | 3697 | } |
56c4a2cb | 3698 | } |
6f036462 TL |
3699 | }); |
3700 | ||
3701 | await teardown.prepare(); | |
3702 | cleanup = teardown.cleanup; | |
3703 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
3704 | |
3705 | await assert.rejects(async () => { | |
3706 | await eslint.lintFiles(["test.md"]); | |
3707 | }, /ESLint configuration of processor in '\.eslintrc\.json' is invalid: 'markdown\/unknown' was not found\./u); | |
3708 | }); | |
3709 | ||
3710 | it("should lint HTML blocks as well with multiple processors if 'overrides[].files' is present.", async () => { | |
6f036462 TL |
3711 | const teardown = createCustomTeardown({ |
3712 | cwd: root, | |
56c4a2cb DC |
3713 | files: { |
3714 | ...commonFiles, | |
6f036462 | 3715 | ".eslintrc.json": { |
56c4a2cb DC |
3716 | plugins: ["markdown", "html"], |
3717 | rules: { semi: "error" }, | |
3718 | overrides: [ | |
3719 | { | |
3720 | files: "*.html", | |
3721 | processor: "html/.html" | |
3722 | }, | |
3723 | { | |
3724 | files: "*.md", | |
3725 | processor: "markdown/.md" | |
3726 | } | |
3727 | ] | |
6f036462 | 3728 | } |
56c4a2cb | 3729 | } |
6f036462 TL |
3730 | }); |
3731 | ||
3732 | await teardown.prepare(); | |
3733 | cleanup = teardown.cleanup; | |
3734 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
3735 | const results = await eslint.lintFiles(["test.md"]); |
3736 | ||
3737 | assert.strictEqual(results.length, 1); | |
3738 | assert.strictEqual(results[0].messages.length, 2); | |
3739 | assert.strictEqual(results[0].messages[0].ruleId, "semi"); // JS block | |
3740 | assert.strictEqual(results[0].messages[0].line, 2); | |
3741 | assert.strictEqual(results[0].messages[1].ruleId, "semi"); // JS block in HTML block | |
3742 | assert.strictEqual(results[0].messages[1].line, 7); | |
3743 | }); | |
3744 | }); | |
3745 | ||
3746 | describe("MODULE_NOT_FOUND error handling", () => { | |
3747 | const cwd = getFixturePath("module-not-found"); | |
3748 | ||
3749 | beforeEach(() => { | |
3750 | eslint = new ESLint({ cwd }); | |
3751 | }); | |
3752 | ||
3753 | it("should throw an error with a message template when 'extends' property has a non-existence JavaScript config.", async () => { | |
3754 | try { | |
3755 | await eslint.lintText("test", { filePath: "extends-js/test.js" }); | |
3756 | } catch (err) { | |
3757 | assert.strictEqual(err.messageTemplate, "extend-config-missing"); | |
3758 | assert.deepStrictEqual(err.messageData, { | |
3759 | configName: "nonexistent-config", | |
3760 | importerName: getFixturePath("module-not-found", "extends-js", ".eslintrc.yml") | |
3761 | }); | |
3762 | return; | |
3763 | } | |
3764 | assert.fail("Expected to throw an error"); | |
3765 | }); | |
3766 | ||
3767 | it("should throw an error with a message template when 'extends' property has a non-existence plugin config.", async () => { | |
3768 | try { | |
3769 | await eslint.lintText("test", { filePath: "extends-plugin/test.js" }); | |
3770 | } catch (err) { | |
3771 | assert.strictEqual(err.code, "MODULE_NOT_FOUND"); | |
3772 | assert.strictEqual(err.messageTemplate, "plugin-missing"); | |
3773 | assert.deepStrictEqual(err.messageData, { | |
3774 | importerName: `extends-plugin${path.sep}.eslintrc.yml`, | |
3775 | pluginName: "eslint-plugin-nonexistent-plugin", | |
3776 | resolvePluginsRelativeTo: path.join(cwd, "extends-plugin") // the directory of the config file. | |
3777 | }); | |
3778 | return; | |
3779 | } | |
3780 | assert.fail("Expected to throw an error"); | |
3781 | }); | |
3782 | ||
3783 | it("should throw an error with a message template when 'plugins' property has a non-existence plugin.", async () => { | |
3784 | try { | |
3785 | await eslint.lintText("test", { filePath: "plugins/test.js" }); | |
3786 | } catch (err) { | |
3787 | assert.strictEqual(err.code, "MODULE_NOT_FOUND"); | |
3788 | assert.strictEqual(err.messageTemplate, "plugin-missing"); | |
3789 | assert.deepStrictEqual(err.messageData, { | |
3790 | importerName: `plugins${path.sep}.eslintrc.yml`, | |
3791 | pluginName: "eslint-plugin-nonexistent-plugin", | |
3792 | resolvePluginsRelativeTo: path.join(cwd, "plugins") // the directory of the config file. | |
3793 | }); | |
3794 | return; | |
3795 | } | |
3796 | assert.fail("Expected to throw an error"); | |
3797 | }); | |
3798 | ||
3799 | it("should throw an error with no message template when a JavaScript config threw a 'MODULE_NOT_FOUND' error.", async () => { | |
3800 | try { | |
3801 | await eslint.lintText("test", { filePath: "throw-in-config-itself/test.js" }); | |
3802 | } catch (err) { | |
3803 | assert.strictEqual(err.code, "MODULE_NOT_FOUND"); | |
3804 | assert.strictEqual(err.messageTemplate, void 0); | |
3805 | return; | |
3806 | } | |
3807 | assert.fail("Expected to throw an error"); | |
3808 | }); | |
3809 | ||
3810 | it("should throw an error with no message template when 'extends' property has a JavaScript config that throws a 'MODULE_NOT_FOUND' error.", async () => { | |
3811 | try { | |
3812 | await eslint.lintText("test", { filePath: "throw-in-extends-js/test.js" }); | |
3813 | } catch (err) { | |
3814 | assert.strictEqual(err.code, "MODULE_NOT_FOUND"); | |
3815 | assert.strictEqual(err.messageTemplate, void 0); | |
3816 | return; | |
3817 | } | |
3818 | assert.fail("Expected to throw an error"); | |
3819 | }); | |
3820 | ||
3821 | it("should throw an error with no message template when 'extends' property has a plugin config that throws a 'MODULE_NOT_FOUND' error.", async () => { | |
3822 | try { | |
3823 | await eslint.lintText("test", { filePath: "throw-in-extends-plugin/test.js" }); | |
3824 | } catch (err) { | |
3825 | assert.strictEqual(err.code, "MODULE_NOT_FOUND"); | |
3826 | assert.strictEqual(err.messageTemplate, void 0); | |
3827 | return; | |
3828 | } | |
3829 | assert.fail("Expected to throw an error"); | |
3830 | }); | |
3831 | ||
3832 | it("should throw an error with no message template when 'plugins' property has a plugin config that throws a 'MODULE_NOT_FOUND' error.", async () => { | |
3833 | try { | |
3834 | await eslint.lintText("test", { filePath: "throw-in-plugins/test.js" }); | |
3835 | } catch (err) { | |
3836 | assert.strictEqual(err.code, "MODULE_NOT_FOUND"); | |
3837 | assert.strictEqual(err.messageTemplate, void 0); | |
3838 | return; | |
3839 | } | |
3840 | assert.fail("Expected to throw an error"); | |
3841 | }); | |
3842 | }); | |
3843 | ||
3844 | describe("with '--rulesdir' option", () => { | |
6f036462 TL |
3845 | |
3846 | const rootPath = getFixturePath("cli-engine/with-rulesdir"); | |
3847 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
3848 | cwd: rootPath, | |
3849 | files: { | |
3850 | "internal-rules/test.js": ` | |
56c4a2cb DC |
3851 | module.exports = context => ({ |
3852 | ExpressionStatement(node) { | |
3853 | context.report({ node, message: "ok" }) | |
3854 | } | |
3855 | }) | |
3856 | `, | |
6f036462 TL |
3857 | ".eslintrc.json": { |
3858 | root: true, | |
3859 | rules: { test: "error" } | |
3860 | }, | |
3861 | "test.js": "console.log('hello')" | |
3862 | } | |
3863 | }); | |
56c4a2cb | 3864 | |
6f036462 TL |
3865 | beforeEach(prepare); |
3866 | afterEach(cleanup); | |
3867 | ||
3868 | ||
3869 | it("should use the configured rules which are defined by '--rulesdir' option.", async () => { | |
3870 | eslint = new ESLint({ | |
3871 | cwd: getPath(), | |
56c4a2cb DC |
3872 | rulePaths: ["internal-rules"] |
3873 | }); | |
3874 | const results = await eslint.lintFiles(["test.js"]); | |
3875 | ||
3876 | assert.strictEqual(results.length, 1); | |
3877 | assert.strictEqual(results[0].messages.length, 1); | |
3878 | assert.strictEqual(results[0].messages[0].message, "ok"); | |
3879 | }); | |
3880 | }); | |
3881 | ||
3882 | describe("glob pattern '[ab].js'", () => { | |
3883 | const root = getFixturePath("cli-engine/unmatched-glob"); | |
3884 | ||
6f036462 TL |
3885 | let cleanup; |
3886 | ||
3887 | beforeEach(() => { | |
3888 | cleanup = () => { }; | |
3889 | }); | |
3890 | ||
3891 | afterEach(() => cleanup()); | |
3892 | ||
56c4a2cb | 3893 | it("should match '[ab].js' if existed.", async () => { |
6f036462 TL |
3894 | |
3895 | const teardown = createCustomTeardown({ | |
3896 | cwd: root, | |
56c4a2cb DC |
3897 | files: { |
3898 | "a.js": "", | |
3899 | "b.js": "", | |
3900 | "ab.js": "", | |
3901 | "[ab].js": "", | |
3902 | ".eslintrc.yml": "root: true" | |
3903 | } | |
6f036462 TL |
3904 | }); |
3905 | ||
3906 | await teardown.prepare(); | |
3907 | cleanup = teardown.cleanup; | |
3908 | ||
3909 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
3910 | const results = await eslint.lintFiles(["[ab].js"]); |
3911 | const filenames = results.map(r => path.basename(r.filePath)); | |
3912 | ||
3913 | assert.deepStrictEqual(filenames, ["[ab].js"]); | |
3914 | }); | |
3915 | ||
3916 | it("should match 'a.js' and 'b.js' if '[ab].js' didn't existed.", async () => { | |
6f036462 TL |
3917 | const teardown = createCustomTeardown({ |
3918 | cwd: root, | |
56c4a2cb DC |
3919 | files: { |
3920 | "a.js": "", | |
3921 | "b.js": "", | |
3922 | "ab.js": "", | |
3923 | ".eslintrc.yml": "root: true" | |
3924 | } | |
6f036462 TL |
3925 | }); |
3926 | ||
3927 | await teardown.prepare(); | |
3928 | cleanup = teardown.cleanup; | |
3929 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
3930 | const results = await eslint.lintFiles(["[ab].js"]); |
3931 | const filenames = results.map(r => path.basename(r.filePath)); | |
3932 | ||
3933 | assert.deepStrictEqual(filenames, ["a.js", "b.js"]); | |
3934 | }); | |
3935 | }); | |
3936 | ||
3937 | describe("with 'noInlineConfig' setting", () => { | |
3938 | const root = getFixturePath("cli-engine/noInlineConfig"); | |
3939 | ||
6f036462 TL |
3940 | let cleanup; |
3941 | ||
3942 | beforeEach(() => { | |
3943 | cleanup = () => { }; | |
3944 | }); | |
3945 | ||
3946 | afterEach(() => cleanup()); | |
3947 | ||
56c4a2cb | 3948 | it("should warn directive comments if 'noInlineConfig' was given.", async () => { |
6f036462 TL |
3949 | const teardown = createCustomTeardown({ |
3950 | cwd: root, | |
56c4a2cb DC |
3951 | files: { |
3952 | "test.js": "/* globals foo */", | |
3953 | ".eslintrc.yml": "noInlineConfig: true" | |
3954 | } | |
6f036462 TL |
3955 | }); |
3956 | ||
3957 | await teardown.prepare(); | |
3958 | cleanup = teardown.cleanup; | |
3959 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
3960 | ||
56c4a2cb DC |
3961 | const results = await eslint.lintFiles(["test.js"]); |
3962 | const messages = results[0].messages; | |
3963 | ||
3964 | assert.strictEqual(messages.length, 1); | |
3965 | assert.strictEqual(messages[0].message, "'/*globals*/' has no effect because you have 'noInlineConfig' setting in your config (.eslintrc.yml)."); | |
3966 | }); | |
3967 | ||
3968 | it("should show the config file what the 'noInlineConfig' came from.", async () => { | |
6f036462 TL |
3969 | const teardown = createCustomTeardown({ |
3970 | cwd: root, | |
56c4a2cb DC |
3971 | files: { |
3972 | "node_modules/eslint-config-foo/index.js": "module.exports = {noInlineConfig: true}", | |
3973 | "test.js": "/* globals foo */", | |
3974 | ".eslintrc.yml": "extends: foo" | |
3975 | } | |
6f036462 TL |
3976 | }); |
3977 | ||
3978 | await teardown.prepare(); | |
3979 | cleanup = teardown.cleanup; | |
3980 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
3981 | ||
56c4a2cb DC |
3982 | const results = await eslint.lintFiles(["test.js"]); |
3983 | const messages = results[0].messages; | |
3984 | ||
3985 | assert.strictEqual(messages.length, 1); | |
3986 | assert.strictEqual(messages[0].message, "'/*globals*/' has no effect because you have 'noInlineConfig' setting in your config (.eslintrc.yml » eslint-config-foo)."); | |
3987 | }); | |
3988 | }); | |
3989 | ||
3990 | describe("with 'reportUnusedDisableDirectives' setting", () => { | |
3991 | const root = getFixturePath("cli-engine/reportUnusedDisableDirectives"); | |
3992 | ||
6f036462 TL |
3993 | let cleanup; |
3994 | ||
3995 | beforeEach(() => { | |
3996 | cleanup = () => { }; | |
3997 | }); | |
3998 | ||
3999 | afterEach(() => cleanup()); | |
4000 | ||
56c4a2cb | 4001 | it("should warn unused 'eslint-disable' comments if 'reportUnusedDisableDirectives' was given.", async () => { |
6f036462 TL |
4002 | const teardown = createCustomTeardown({ |
4003 | cwd: root, | |
56c4a2cb DC |
4004 | files: { |
4005 | "test.js": "/* eslint-disable eqeqeq */", | |
4006 | ".eslintrc.yml": "reportUnusedDisableDirectives: true" | |
4007 | } | |
6f036462 TL |
4008 | }); |
4009 | ||
4010 | ||
4011 | await teardown.prepare(); | |
4012 | cleanup = teardown.cleanup; | |
4013 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
4014 | ||
56c4a2cb DC |
4015 | const results = await eslint.lintFiles(["test.js"]); |
4016 | const messages = results[0].messages; | |
4017 | ||
4018 | assert.strictEqual(messages.length, 1); | |
4019 | assert.strictEqual(messages[0].severity, 1); | |
4020 | assert.strictEqual(messages[0].message, "Unused eslint-disable directive (no problems were reported from 'eqeqeq')."); | |
4021 | }); | |
4022 | ||
4023 | describe("the runtime option overrides config files.", () => { | |
4024 | it("should not warn unused 'eslint-disable' comments if 'reportUnusedDisableDirectives=off' was given in runtime.", async () => { | |
6f036462 TL |
4025 | const teardown = createCustomTeardown({ |
4026 | cwd: root, | |
56c4a2cb DC |
4027 | files: { |
4028 | "test.js": "/* eslint-disable eqeqeq */", | |
4029 | ".eslintrc.yml": "reportUnusedDisableDirectives: true" | |
4030 | } | |
6f036462 TL |
4031 | }); |
4032 | ||
4033 | await teardown.prepare(); | |
4034 | cleanup = teardown.cleanup; | |
4035 | ||
4036 | eslint = new ESLint({ | |
4037 | cwd: teardown.getPath(), | |
4038 | reportUnusedDisableDirectives: "off" | |
4039 | }); | |
4040 | ||
56c4a2cb DC |
4041 | const results = await eslint.lintFiles(["test.js"]); |
4042 | const messages = results[0].messages; | |
4043 | ||
4044 | assert.strictEqual(messages.length, 0); | |
4045 | }); | |
4046 | ||
4047 | it("should warn unused 'eslint-disable' comments as error if 'reportUnusedDisableDirectives=error' was given in runtime.", async () => { | |
6f036462 TL |
4048 | const teardown = createCustomTeardown({ |
4049 | cwd: root, | |
56c4a2cb DC |
4050 | files: { |
4051 | "test.js": "/* eslint-disable eqeqeq */", | |
4052 | ".eslintrc.yml": "reportUnusedDisableDirectives: true" | |
4053 | } | |
6f036462 TL |
4054 | }); |
4055 | ||
4056 | await teardown.prepare(); | |
4057 | cleanup = teardown.cleanup; | |
4058 | ||
4059 | eslint = new ESLint({ | |
4060 | cwd: teardown.getPath(), | |
4061 | reportUnusedDisableDirectives: "error" | |
4062 | }); | |
4063 | ||
56c4a2cb DC |
4064 | const results = await eslint.lintFiles(["test.js"]); |
4065 | const messages = results[0].messages; | |
4066 | ||
4067 | assert.strictEqual(messages.length, 1); | |
4068 | assert.strictEqual(messages[0].severity, 2); | |
4069 | assert.strictEqual(messages[0].message, "Unused eslint-disable directive (no problems were reported from 'eqeqeq')."); | |
4070 | }); | |
4071 | }); | |
4072 | }); | |
4073 | ||
4074 | describe("with 'overrides[*].extends' setting on deep locations", () => { | |
4075 | const root = getFixturePath("cli-engine/deeply-overrides-i-extends"); | |
6f036462 TL |
4076 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
4077 | cwd: root, | |
4078 | files: { | |
4079 | "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ | |
4080 | overrides: [{ files: ["*test*"], extends: "two" }] | |
4081 | })}`, | |
4082 | "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ | |
4083 | overrides: [{ files: ["*.js"], extends: "three" }] | |
4084 | })}`, | |
4085 | "node_modules/eslint-config-three/index.js": `module.exports = ${JSON.stringify({ | |
4086 | rules: { "no-console": "error" } | |
4087 | })}`, | |
4088 | "test.js": "console.log('hello')", | |
4089 | ".eslintrc.yml": "extends: one" | |
4090 | } | |
4091 | }); | |
4092 | ||
4093 | beforeEach(prepare); | |
4094 | afterEach(cleanup); | |
56c4a2cb DC |
4095 | |
4096 | it("should not throw.", async () => { | |
6f036462 | 4097 | eslint = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
4098 | const results = await eslint.lintFiles(["test.js"]); |
4099 | const messages = results[0].messages; | |
4100 | ||
4101 | assert.strictEqual(messages.length, 1); | |
4102 | assert.strictEqual(messages[0].ruleId, "no-console"); | |
4103 | }); | |
4104 | }); | |
4105 | ||
4106 | describe("don't ignore the entry directory.", () => { | |
4107 | const root = getFixturePath("cli-engine/dont-ignore-entry-dir"); | |
4108 | ||
6f036462 TL |
4109 | let cleanup; |
4110 | ||
4111 | beforeEach(() => { | |
4112 | cleanup = () => { }; | |
4113 | }); | |
4114 | ||
4115 | afterEach(async () => { | |
4116 | await cleanup(); | |
4117 | ||
4118 | const configFilePath = path.resolve(root, "../.eslintrc.json"); | |
4119 | ||
4120 | if (shell.test("-e", configFilePath)) { | |
4121 | shell.rm(configFilePath); | |
4122 | } | |
4123 | }); | |
4124 | ||
56c4a2cb | 4125 | it("'lintFiles(\".\")' should not load config files from outside of \".\".", async () => { |
6f036462 TL |
4126 | const teardown = createCustomTeardown({ |
4127 | cwd: root, | |
56c4a2cb DC |
4128 | files: { |
4129 | "../.eslintrc.json": "BROKEN FILE", | |
4130 | ".eslintrc.json": JSON.stringify({ root: true }), | |
4131 | "index.js": "console.log(\"hello\")" | |
4132 | } | |
6f036462 TL |
4133 | }); |
4134 | ||
4135 | await teardown.prepare(); | |
4136 | cleanup = teardown.cleanup; | |
4137 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
4138 | |
4139 | // Don't throw "failed to load config file" error. | |
4140 | await eslint.lintFiles("."); | |
4141 | }); | |
4142 | ||
4143 | it("'lintFiles(\".\")' should not ignore '.' even if 'ignorePatterns' contains it.", async () => { | |
6f036462 TL |
4144 | const teardown = createCustomTeardown({ |
4145 | cwd: root, | |
56c4a2cb | 4146 | files: { |
6f036462 TL |
4147 | "../.eslintrc.json": { ignorePatterns: ["/dont-ignore-entry-dir"] }, |
4148 | ".eslintrc.json": { root: true }, | |
56c4a2cb DC |
4149 | "index.js": "console.log(\"hello\")" |
4150 | } | |
6f036462 TL |
4151 | }); |
4152 | ||
4153 | await teardown.prepare(); | |
4154 | cleanup = teardown.cleanup; | |
4155 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
4156 | |
4157 | // Don't throw "file not found" error. | |
4158 | await eslint.lintFiles("."); | |
4159 | }); | |
4160 | ||
4161 | it("'lintFiles(\"subdir\")' should not ignore './subdir' even if 'ignorePatterns' contains it.", async () => { | |
6f036462 TL |
4162 | const teardown = createCustomTeardown({ |
4163 | cwd: root, | |
56c4a2cb | 4164 | files: { |
6f036462 TL |
4165 | ".eslintrc.json": { ignorePatterns: ["/subdir"] }, |
4166 | "subdir/.eslintrc.json": { root: true }, | |
56c4a2cb DC |
4167 | "subdir/index.js": "console.log(\"hello\")" |
4168 | } | |
6f036462 TL |
4169 | }); |
4170 | ||
4171 | await teardown.prepare(); | |
4172 | cleanup = teardown.cleanup; | |
4173 | eslint = new ESLint({ cwd: teardown.getPath() }); | |
56c4a2cb DC |
4174 | |
4175 | // Don't throw "file not found" error. | |
4176 | await eslint.lintFiles("subdir"); | |
4177 | }); | |
4178 | }); | |
4179 | ||
4180 | it("should throw if non-boolean value is given to 'options.warnIgnored' option", async () => { | |
4181 | eslint = new ESLint(); | |
4182 | await assert.rejects(() => eslint.lintFiles(777), /'patterns' must be a non-empty string or an array of non-empty strings/u); | |
4183 | await assert.rejects(() => eslint.lintFiles([null]), /'patterns' must be a non-empty string or an array of non-empty strings/u); | |
4184 | }); | |
4185 | }); | |
4186 | ||
4187 | describe("calculateConfigForFile", () => { | |
4188 | it("should return the info from Config#getConfig when called", async () => { | |
4189 | const options = { | |
4190 | overrideConfigFile: getFixturePath("configurations", "quotes-error.json") | |
4191 | }; | |
4192 | const engine = new ESLint(options); | |
4193 | const filePath = getFixturePath("single-quoted.js"); | |
4194 | const actualConfig = await engine.calculateConfigForFile(filePath); | |
4195 | const expectedConfig = new CascadingConfigArrayFactory({ specificConfigPath: options.overrideConfigFile }) | |
4196 | .getConfigArrayForFile(filePath) | |
4197 | .extractConfig(filePath) | |
4198 | .toCompatibleObjectAsConfigFileContent(); | |
4199 | ||
4200 | assert.deepStrictEqual(actualConfig, expectedConfig); | |
4201 | }); | |
4202 | ||
5422a9cc TL |
4203 | it("should return the config for a file that doesn't exist", async () => { |
4204 | const engine = new ESLint(); | |
4205 | const filePath = getFixturePath("does_not_exist.js"); | |
4206 | const existingSiblingFilePath = getFixturePath("single-quoted.js"); | |
4207 | const actualConfig = await engine.calculateConfigForFile(filePath); | |
4208 | const expectedConfig = await engine.calculateConfigForFile(existingSiblingFilePath); | |
4209 | ||
4210 | assert.deepStrictEqual(actualConfig, expectedConfig); | |
4211 | }); | |
4212 | ||
4213 | it("should return the config for a virtual file that is a child of an existing file", async () => { | |
4214 | const engine = new ESLint(); | |
4215 | const parentFileName = "single-quoted.js"; | |
4216 | const filePath = getFixturePath(parentFileName, "virtual.js"); // single-quoted.js/virtual.js | |
4217 | const parentFilePath = getFixturePath(parentFileName); | |
4218 | const actualConfig = await engine.calculateConfigForFile(filePath); | |
4219 | const expectedConfig = await engine.calculateConfigForFile(parentFilePath); | |
4220 | ||
4221 | assert.deepStrictEqual(actualConfig, expectedConfig); | |
4222 | }); | |
56c4a2cb DC |
4223 | |
4224 | it("should return the config when run from within a subdir", async () => { | |
4225 | const options = { | |
4226 | cwd: getFixturePath("config-hierarchy", "root-true", "parent", "root", "subdir") | |
4227 | }; | |
4228 | const engine = new ESLint(options); | |
4229 | const filePath = getFixturePath("config-hierarchy", "root-true", "parent", "root", ".eslintrc"); | |
4230 | const actualConfig = await engine.calculateConfigForFile("./.eslintrc"); | |
4231 | const expectedConfig = new CascadingConfigArrayFactory(options) | |
4232 | .getConfigArrayForFile(filePath) | |
4233 | .extractConfig(filePath) | |
4234 | .toCompatibleObjectAsConfigFileContent(); | |
4235 | ||
4236 | assert.deepStrictEqual(actualConfig, expectedConfig); | |
4237 | }); | |
4238 | ||
4239 | it("should throw an error if a directory path was given.", async () => { | |
4240 | const engine = new ESLint(); | |
4241 | ||
4242 | try { | |
4243 | await engine.calculateConfigForFile("."); | |
4244 | } catch (error) { | |
4245 | assert.strictEqual(error.messageTemplate, "print-config-with-directory-path"); | |
4246 | return; | |
4247 | } | |
4248 | assert.fail("should throw an error"); | |
4249 | }); | |
4250 | ||
4251 | it("should throw if non-string value is given to 'filePath' parameter", async () => { | |
4252 | const eslint = new ESLint(); | |
4253 | ||
4254 | await assert.rejects(() => eslint.calculateConfigForFile(null), /'filePath' must be a non-empty string/u); | |
4255 | }); | |
6f036462 TL |
4256 | |
4257 | // https://github.com/eslint/eslint/issues/13793 | |
4258 | it("should throw with an invalid built-in rule config", async () => { | |
4259 | const options = { | |
4260 | baseConfig: { | |
4261 | rules: { | |
4262 | "no-alert": ["error", { | |
4263 | thisDoesNotExist: true | |
4264 | }] | |
4265 | } | |
4266 | } | |
4267 | }; | |
4268 | const engine = new ESLint(options); | |
4269 | const filePath = getFixturePath("single-quoted.js"); | |
4270 | ||
4271 | await assert.rejects( | |
4272 | () => engine.calculateConfigForFile(filePath), | |
4273 | /Configuration for rule "no-alert" is invalid:/u | |
4274 | ); | |
4275 | }); | |
56c4a2cb DC |
4276 | }); |
4277 | ||
4278 | describe("isPathIgnored", () => { | |
4279 | it("should check if the given path is ignored", async () => { | |
4280 | const engine = new ESLint({ | |
4281 | ignorePath: getFixturePath(".eslintignore2"), | |
4282 | cwd: getFixturePath() | |
4283 | }); | |
4284 | ||
4285 | assert(await engine.isPathIgnored("undef.js")); | |
4286 | assert(!await engine.isPathIgnored("passing.js")); | |
4287 | }); | |
4288 | ||
4289 | it("should return false if ignoring is disabled", async () => { | |
4290 | const engine = new ESLint({ | |
4291 | ignore: false, | |
4292 | ignorePath: getFixturePath(".eslintignore2"), | |
4293 | cwd: getFixturePath() | |
4294 | }); | |
4295 | ||
4296 | assert(!await engine.isPathIgnored("undef.js")); | |
4297 | }); | |
4298 | ||
4299 | // https://github.com/eslint/eslint/issues/5547 | |
4300 | it("should return true for default ignores even if ignoring is disabled", async () => { | |
4301 | const engine = new ESLint({ | |
4302 | ignore: false, | |
4303 | cwd: getFixturePath("cli-engine") | |
4304 | }); | |
4305 | ||
4306 | assert(await engine.isPathIgnored("node_modules/foo.js")); | |
4307 | }); | |
4308 | ||
4309 | describe("about the default ignore patterns", () => { | |
4310 | it("should always apply defaultPatterns if ignore option is true", async () => { | |
4311 | const cwd = getFixturePath("ignored-paths"); | |
4312 | const engine = new ESLint({ cwd }); | |
4313 | ||
4314 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/package/file.js"))); | |
4315 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/node_modules/package/file.js"))); | |
4316 | }); | |
4317 | ||
4318 | it("should still apply defaultPatterns if ignore option is is false", async () => { | |
4319 | const cwd = getFixturePath("ignored-paths"); | |
4320 | const engine = new ESLint({ ignore: false, cwd }); | |
4321 | ||
4322 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/package/file.js"))); | |
4323 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/node_modules/package/file.js"))); | |
4324 | }); | |
4325 | ||
4326 | it("should allow subfolders of defaultPatterns to be unignored by ignorePattern", async () => { | |
4327 | const cwd = getFixturePath("ignored-paths"); | |
4328 | const engine = new ESLint({ | |
4329 | cwd, | |
4330 | overrideConfig: { | |
4331 | ignorePatterns: "!/node_modules/package" | |
4332 | } | |
4333 | }); | |
4334 | ||
4335 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js"))); | |
4336 | }); | |
4337 | ||
4338 | it("should allow subfolders of defaultPatterns to be unignored by ignorePath", async () => { | |
4339 | const cwd = getFixturePath("ignored-paths"); | |
4340 | const engine = new ESLint({ cwd, ignorePath: getFixturePath("ignored-paths", ".eslintignoreWithUnignoredDefaults") }); | |
4341 | ||
4342 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js"))); | |
4343 | }); | |
4344 | ||
4345 | it("should ignore dotfiles", async () => { | |
4346 | const cwd = getFixturePath("ignored-paths"); | |
4347 | const engine = new ESLint({ cwd }); | |
4348 | ||
4349 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo"))); | |
4350 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar"))); | |
4351 | }); | |
4352 | ||
4353 | it("should ignore directories beginning with a dot", async () => { | |
4354 | const cwd = getFixturePath("ignored-paths"); | |
4355 | const engine = new ESLint({ cwd }); | |
4356 | ||
4357 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo/bar"))); | |
4358 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar/baz"))); | |
4359 | }); | |
4360 | ||
4361 | it("should still ignore dotfiles when ignore option disabled", async () => { | |
4362 | const cwd = getFixturePath("ignored-paths"); | |
4363 | const engine = new ESLint({ ignore: false, cwd }); | |
4364 | ||
4365 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo"))); | |
4366 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar"))); | |
4367 | }); | |
4368 | ||
4369 | it("should still ignore directories beginning with a dot when ignore option disabled", async () => { | |
4370 | const cwd = getFixturePath("ignored-paths"); | |
4371 | const engine = new ESLint({ ignore: false, cwd }); | |
4372 | ||
4373 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo/bar"))); | |
4374 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar/baz"))); | |
4375 | }); | |
4376 | ||
4377 | it("should not ignore absolute paths containing '..'", async () => { | |
4378 | const cwd = getFixturePath("ignored-paths"); | |
4379 | const engine = new ESLint({ cwd }); | |
4380 | ||
4381 | assert(!await engine.isPathIgnored(`${getFixturePath("ignored-paths", "foo")}/../unignored.js`)); | |
4382 | }); | |
4383 | ||
4384 | it("should ignore /node_modules/ relative to .eslintignore when loaded", async () => { | |
4385 | const cwd = getFixturePath("ignored-paths"); | |
4386 | const engine = new ESLint({ ignorePath: getFixturePath("ignored-paths", ".eslintignore"), cwd }); | |
4387 | ||
4388 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "existing.js"))); | |
4389 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo", "node_modules", "existing.js"))); | |
4390 | }); | |
4391 | ||
4392 | it("should ignore /node_modules/ relative to cwd without an .eslintignore", async () => { | |
4393 | const cwd = getFixturePath("ignored-paths", "no-ignore-file"); | |
4394 | const engine = new ESLint({ cwd }); | |
4395 | ||
4396 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "no-ignore-file", "node_modules", "existing.js"))); | |
4397 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "no-ignore-file", "foo", "node_modules", "existing.js"))); | |
4398 | }); | |
4399 | }); | |
4400 | ||
4401 | describe("with no .eslintignore file", () => { | |
4402 | it("should not travel to parent directories to find .eslintignore when it's missing and cwd is provided", async () => { | |
4403 | const cwd = getFixturePath("ignored-paths", "configurations"); | |
4404 | const engine = new ESLint({ cwd }); | |
4405 | ||
f2a92ac6 | 4406 | // an .eslintignore in parent directories includes `*.js`, but don't load it. |
56c4a2cb DC |
4407 | assert(!await engine.isPathIgnored("foo.js")); |
4408 | assert(await engine.isPathIgnored("node_modules/foo.js")); | |
4409 | }); | |
4410 | ||
4411 | it("should return false for files outside of the cwd (with no ignore file provided)", async () => { | |
4412 | ||
4413 | // Default ignore patterns should not inadvertently ignore files in parent directories | |
4414 | const engine = new ESLint({ cwd: getFixturePath("ignored-paths", "no-ignore-file") }); | |
4415 | ||
4416 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); | |
4417 | }); | |
4418 | }); | |
4419 | ||
4420 | describe("with .eslintignore file or package.json file", () => { | |
4421 | it("should load .eslintignore from cwd when explicitly passed", async () => { | |
4422 | const cwd = getFixturePath("ignored-paths"); | |
4423 | const engine = new ESLint({ cwd }); | |
4424 | ||
4425 | // `${cwd}/.eslintignore` includes `sampleignorepattern`. | |
4426 | assert(await engine.isPathIgnored("sampleignorepattern")); | |
4427 | }); | |
4428 | ||
4429 | it("should use package.json's eslintIgnore files if no specified .eslintignore file", async () => { | |
4430 | const cwd = getFixturePath("ignored-paths", "package-json-ignore"); | |
4431 | const engine = new ESLint({ cwd }); | |
4432 | ||
4433 | assert(await engine.isPathIgnored("hello.js")); | |
4434 | assert(await engine.isPathIgnored("world.js")); | |
4435 | }); | |
4436 | ||
4437 | it("should use correct message template if failed to parse package.json", () => { | |
4438 | const cwd = getFixturePath("ignored-paths", "broken-package-json"); | |
4439 | ||
4440 | assert.throws(() => { | |
4441 | try { | |
609c276f | 4442 | // eslint-disable-next-line no-new -- Check for error |
56c4a2cb DC |
4443 | new ESLint({ cwd }); |
4444 | } catch (error) { | |
4445 | assert.strictEqual(error.messageTemplate, "failed-to-read-json"); | |
4446 | throw error; | |
4447 | } | |
4448 | }); | |
4449 | }); | |
4450 | ||
4451 | it("should not use package.json's eslintIgnore files if specified .eslintignore file", async () => { | |
4452 | const cwd = getFixturePath("ignored-paths"); | |
4453 | const engine = new ESLint({ cwd }); | |
4454 | ||
4455 | /* | |
4456 | * package.json includes `hello.js` and `world.js`. | |
4457 | * .eslintignore includes `sampleignorepattern`. | |
4458 | */ | |
4459 | assert(!await engine.isPathIgnored("hello.js")); | |
4460 | assert(!await engine.isPathIgnored("world.js")); | |
4461 | assert(await engine.isPathIgnored("sampleignorepattern")); | |
4462 | }); | |
4463 | ||
4464 | it("should error if package.json's eslintIgnore is not an array of file paths", () => { | |
4465 | const cwd = getFixturePath("ignored-paths", "bad-package-json-ignore"); | |
4466 | ||
4467 | assert.throws(() => { | |
609c276f | 4468 | // eslint-disable-next-line no-new -- Check for throwing |
56c4a2cb DC |
4469 | new ESLint({ cwd }); |
4470 | }, /Package\.json eslintIgnore property requires an array of paths/u); | |
4471 | }); | |
4472 | }); | |
4473 | ||
4474 | describe("with --ignore-pattern option", () => { | |
4475 | it("should accept a string for options.ignorePattern", async () => { | |
4476 | const cwd = getFixturePath("ignored-paths", "ignore-pattern"); | |
4477 | const engine = new ESLint({ | |
4478 | overrideConfig: { | |
4479 | ignorePatterns: "ignore-me.txt" | |
4480 | }, | |
4481 | cwd | |
4482 | }); | |
4483 | ||
4484 | assert(await engine.isPathIgnored("ignore-me.txt")); | |
4485 | }); | |
4486 | ||
4487 | it("should accept an array for options.ignorePattern", async () => { | |
4488 | const engine = new ESLint({ | |
4489 | overrideConfig: { | |
4490 | ignorePatterns: ["a", "b"] | |
4491 | }, | |
4492 | useEslintrc: false | |
4493 | }); | |
4494 | ||
4495 | assert(await engine.isPathIgnored("a")); | |
4496 | assert(await engine.isPathIgnored("b")); | |
4497 | assert(!await engine.isPathIgnored("c")); | |
4498 | }); | |
4499 | ||
4500 | it("should return true for files which match an ignorePattern even if they do not exist on the filesystem", async () => { | |
4501 | const cwd = getFixturePath("ignored-paths"); | |
4502 | const engine = new ESLint({ | |
4503 | overrideConfig: { | |
4504 | ignorePatterns: "not-a-file" | |
4505 | }, | |
4506 | cwd | |
4507 | }); | |
4508 | ||
4509 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "not-a-file"))); | |
4510 | }); | |
4511 | ||
4512 | it("should return true for file matching an ignore pattern exactly", async () => { | |
4513 | const cwd = getFixturePath("ignored-paths"); | |
4514 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "undef.js" }, cwd }); | |
4515 | ||
4516 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); | |
4517 | }); | |
4518 | ||
4519 | it("should return false for file matching an invalid ignore pattern with leading './'", async () => { | |
4520 | const cwd = getFixturePath("ignored-paths"); | |
4521 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "./undef.js" }, cwd }); | |
4522 | ||
4523 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); | |
4524 | }); | |
4525 | ||
4526 | it("should return false for file in subfolder of cwd matching an ignore pattern with leading '/'", async () => { | |
4527 | const cwd = getFixturePath("ignored-paths"); | |
4528 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "/undef.js" }, cwd }); | |
4529 | ||
4530 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir", "undef.js"))); | |
4531 | }); | |
4532 | ||
4533 | it("should return true for file matching a child of an ignore pattern", async () => { | |
4534 | const cwd = getFixturePath("ignored-paths"); | |
4535 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "ignore-pattern" }, cwd }); | |
4536 | ||
4537 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "ignore-pattern", "ignore-me.txt"))); | |
4538 | }); | |
4539 | ||
4540 | it("should return true for file matching a grandchild of an ignore pattern", async () => { | |
4541 | const cwd = getFixturePath("ignored-paths"); | |
4542 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "ignore-pattern" }, cwd }); | |
4543 | ||
4544 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "ignore-pattern", "subdir", "ignore-me.txt"))); | |
4545 | }); | |
4546 | ||
4547 | it("should return false for file not matching any ignore pattern", async () => { | |
4548 | const cwd = getFixturePath("ignored-paths"); | |
4549 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "failing.js" }, cwd }); | |
4550 | ||
4551 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "unignored.js"))); | |
4552 | }); | |
4553 | ||
4554 | it("two globstar '**' ignore pattern should ignore files in nested directories", async () => { | |
4555 | const cwd = getFixturePath("ignored-paths"); | |
4556 | const engine = new ESLint({ overrideConfig: { ignorePatterns: "**/*.js" }, cwd }); | |
4557 | ||
4558 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); | |
4559 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar.js"))); | |
4560 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar/baz.js"))); | |
4561 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "foo.j2"))); | |
4562 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar.j2"))); | |
4563 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar/baz.j2"))); | |
4564 | }); | |
4565 | }); | |
4566 | ||
4567 | describe("with --ignore-path option", () => { | |
4568 | it("initialization with ignorePath should work when cwd is a parent directory", async () => { | |
4569 | const cwd = getFixturePath("ignored-paths"); | |
4570 | const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); | |
4571 | const engine = new ESLint({ ignorePath, cwd }); | |
4572 | ||
4573 | assert(await engine.isPathIgnored("custom-name/foo.js")); | |
4574 | }); | |
4575 | ||
4576 | it("initialization with ignorePath should work when the file is in the cwd", async () => { | |
4577 | const cwd = getFixturePath("ignored-paths", "custom-name"); | |
4578 | const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); | |
4579 | const engine = new ESLint({ ignorePath, cwd }); | |
4580 | ||
4581 | assert(await engine.isPathIgnored("foo.js")); | |
4582 | }); | |
4583 | ||
4584 | it("initialization with ignorePath should work when cwd is a subdirectory", async () => { | |
4585 | const cwd = getFixturePath("ignored-paths", "custom-name", "subdirectory"); | |
4586 | const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); | |
4587 | const engine = new ESLint({ ignorePath, cwd }); | |
4588 | ||
4589 | assert(await engine.isPathIgnored("../custom-name/foo.js")); | |
4590 | }); | |
4591 | ||
4592 | it("initialization with invalid file should throw error", () => { | |
4593 | const cwd = getFixturePath("ignored-paths"); | |
4594 | const ignorePath = getFixturePath("ignored-paths", "not-a-directory", ".foobaz"); | |
4595 | ||
4596 | assert.throws(() => { | |
609c276f | 4597 | // eslint-disable-next-line no-new -- Check for throwing |
56c4a2cb DC |
4598 | new ESLint({ ignorePath, cwd }); |
4599 | }, /Cannot read \.eslintignore file/u); | |
4600 | }); | |
4601 | ||
4602 | it("should return false for files outside of ignorePath's directory", async () => { | |
4603 | const cwd = getFixturePath("ignored-paths"); | |
4604 | const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); | |
4605 | const engine = new ESLint({ ignorePath, cwd }); | |
4606 | ||
4607 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); | |
4608 | }); | |
4609 | ||
4610 | it("should resolve relative paths from CWD", async () => { | |
4611 | const cwd = getFixturePath("ignored-paths", "subdir"); | |
4612 | const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); | |
4613 | const engine = new ESLint({ ignorePath, cwd }); | |
4614 | ||
4615 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); | |
4616 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); | |
4617 | }); | |
4618 | ||
4619 | it("should resolve relative paths from CWD when it's in a child directory", async () => { | |
4620 | const cwd = getFixturePath("ignored-paths"); | |
4621 | const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); | |
4622 | const engine = new ESLint({ ignorePath, cwd }); | |
4623 | ||
4624 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); | |
4625 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); | |
4626 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); | |
4627 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/foo.js"))); | |
4628 | ||
4629 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/bar.js"))); | |
4630 | }); | |
4631 | ||
4632 | it("should resolve relative paths from CWD when it contains negated globs", async () => { | |
4633 | const cwd = getFixturePath("ignored-paths"); | |
4634 | const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); | |
4635 | const engine = new ESLint({ ignorePath, cwd }); | |
4636 | ||
4637 | assert(await engine.isPathIgnored("subdir/blah.txt")); | |
4638 | assert(await engine.isPathIgnored("blah.txt")); | |
4639 | assert(await engine.isPathIgnored("subdir/bar.txt")); | |
4640 | assert(!await engine.isPathIgnored("bar.txt")); | |
4641 | assert(!await engine.isPathIgnored("subdir/baz.txt")); | |
4642 | assert(!await engine.isPathIgnored("baz.txt")); | |
4643 | }); | |
4644 | ||
4645 | it("should resolve default ignore patterns from the CWD even when the ignorePath is in a subdirectory", async () => { | |
4646 | const cwd = getFixturePath("ignored-paths"); | |
4647 | const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); | |
4648 | const engine = new ESLint({ ignorePath, cwd }); | |
4649 | ||
4650 | assert(await engine.isPathIgnored("node_modules/blah.js")); | |
4651 | }); | |
4652 | ||
4653 | it("should resolve default ignore patterns from the CWD even when the ignorePath is in a parent directory", async () => { | |
4654 | const cwd = getFixturePath("ignored-paths", "subdir"); | |
4655 | const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); | |
4656 | const engine = new ESLint({ ignorePath, cwd }); | |
4657 | ||
4658 | assert(await engine.isPathIgnored("node_modules/blah.js")); | |
4659 | }); | |
4660 | ||
4661 | it("should handle .eslintignore which contains CRLF correctly.", async () => { | |
4662 | const ignoreFileContent = fs.readFileSync(getFixturePath("ignored-paths", "crlf/.eslintignore"), "utf8"); | |
4663 | ||
4664 | assert(ignoreFileContent.includes("\r"), "crlf/.eslintignore should contains CR."); | |
4665 | const cwd = getFixturePath("ignored-paths"); | |
4666 | const ignorePath = getFixturePath("ignored-paths", "crlf/.eslintignore"); | |
4667 | const engine = new ESLint({ ignorePath, cwd }); | |
4668 | ||
4669 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide1/a.js"))); | |
4670 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide2/a.js"))); | |
4671 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide3/a.js"))); | |
4672 | }); | |
4673 | ||
4674 | it("should not include comments in ignore rules", async () => { | |
4675 | const cwd = getFixturePath("ignored-paths"); | |
4676 | const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithComments"); | |
4677 | const engine = new ESLint({ ignorePath, cwd }); | |
4678 | ||
4679 | assert(!await engine.isPathIgnored("# should be ignored")); | |
4680 | assert(await engine.isPathIgnored("this_one_not")); | |
4681 | }); | |
4682 | ||
4683 | it("should ignore a non-negated pattern", async () => { | |
4684 | const cwd = getFixturePath("ignored-paths"); | |
4685 | const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithNegation"); | |
4686 | const engine = new ESLint({ ignorePath, cwd }); | |
4687 | ||
4688 | assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "negation", "ignore.js"))); | |
4689 | }); | |
4690 | ||
4691 | it("should not ignore a negated pattern", async () => { | |
4692 | const cwd = getFixturePath("ignored-paths"); | |
4693 | const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithNegation"); | |
4694 | const engine = new ESLint({ ignorePath, cwd }); | |
4695 | ||
4696 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "negation", "unignore.js"))); | |
4697 | }); | |
8f9d1d4d DC |
4698 | |
4699 | // https://github.com/eslint/eslint/issues/15642 | |
4700 | it("should correctly handle patterns with escaped brackets", async () => { | |
4701 | const cwd = getFixturePath("ignored-paths"); | |
4702 | const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithEscapedBrackets"); | |
4703 | const engine = new ESLint({ ignorePath, cwd }); | |
4704 | ||
4705 | const subdir = "brackets"; | |
4706 | ||
4707 | assert( | |
4708 | !await engine.isPathIgnored(getFixturePath("ignored-paths", subdir, "index.js")), | |
4709 | `'${subdir}/index.js' should not be ignored` | |
4710 | ); | |
4711 | ||
4712 | for (const filename of ["[index.js", "index].js", "[index].js"]) { | |
4713 | assert( | |
4714 | await engine.isPathIgnored(getFixturePath("ignored-paths", subdir, filename)), | |
4715 | `'${subdir}/${filename}' should be ignored` | |
4716 | ); | |
4717 | } | |
4718 | ||
4719 | }); | |
56c4a2cb DC |
4720 | }); |
4721 | ||
4722 | describe("with --ignore-path option and --ignore-pattern option", () => { | |
4723 | it("should return false for ignored file when unignored with ignore pattern", async () => { | |
4724 | const cwd = getFixturePath("ignored-paths"); | |
4725 | const engine = new ESLint({ | |
4726 | ignorePath: getFixturePath("ignored-paths", ".eslintignore"), | |
4727 | overrideConfig: { | |
4728 | ignorePatterns: "!sampleignorepattern" | |
4729 | }, | |
4730 | cwd | |
4731 | }); | |
4732 | ||
4733 | assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "sampleignorepattern"))); | |
4734 | }); | |
4735 | }); | |
4736 | ||
4737 | it("should throw if non-string value is given to 'filePath' parameter", async () => { | |
4738 | const eslint = new ESLint(); | |
4739 | ||
4740 | await assert.rejects(() => eslint.isPathIgnored(null), /'filePath' must be a non-empty string/u); | |
4741 | }); | |
4742 | }); | |
4743 | ||
4744 | describe("loadFormatter()", () => { | |
4745 | it("should return a formatter object when a bundled formatter is requested", async () => { | |
4746 | const engine = new ESLint(); | |
4747 | const formatter = await engine.loadFormatter("compact"); | |
4748 | ||
4749 | assert.strictEqual(typeof formatter, "object"); | |
4750 | assert.strictEqual(typeof formatter.format, "function"); | |
4751 | }); | |
4752 | ||
4753 | it("should return a formatter object when no argument is passed", async () => { | |
4754 | const engine = new ESLint(); | |
4755 | const formatter = await engine.loadFormatter(); | |
4756 | ||
4757 | assert.strictEqual(typeof formatter, "object"); | |
4758 | assert.strictEqual(typeof formatter.format, "function"); | |
4759 | }); | |
4760 | ||
4761 | it("should return a formatter object when a custom formatter is requested", async () => { | |
4762 | const engine = new ESLint(); | |
4763 | const formatter = await engine.loadFormatter(getFixturePath("formatters", "simple.js")); | |
4764 | ||
4765 | assert.strictEqual(typeof formatter, "object"); | |
4766 | assert.strictEqual(typeof formatter.format, "function"); | |
4767 | }); | |
4768 | ||
4769 | it("should return a formatter object when a custom formatter is requested, also if the path has backslashes", async () => { | |
4770 | const engine = new ESLint({ | |
4771 | cwd: path.join(fixtureDir, "..") | |
4772 | }); | |
4773 | const formatter = await engine.loadFormatter(".\\fixtures\\formatters\\simple.js"); | |
4774 | ||
4775 | assert.strictEqual(typeof formatter, "object"); | |
4776 | assert.strictEqual(typeof formatter.format, "function"); | |
4777 | }); | |
4778 | ||
4779 | it("should return a formatter object when a formatter prefixed with eslint-formatter is requested", async () => { | |
4780 | const engine = new ESLint({ | |
4781 | cwd: getFixturePath("cli-engine") | |
4782 | }); | |
4783 | const formatter = await engine.loadFormatter("bar"); | |
4784 | ||
4785 | assert.strictEqual(typeof formatter, "object"); | |
4786 | assert.strictEqual(typeof formatter.format, "function"); | |
4787 | }); | |
4788 | ||
4789 | it("should return a formatter object when a formatter is requested, also when the eslint-formatter prefix is included in the format argument", async () => { | |
4790 | const engine = new ESLint({ | |
4791 | cwd: getFixturePath("cli-engine") | |
4792 | }); | |
4793 | const formatter = await engine.loadFormatter("eslint-formatter-bar"); | |
4794 | ||
4795 | assert.strictEqual(typeof formatter, "object"); | |
4796 | assert.strictEqual(typeof formatter.format, "function"); | |
4797 | }); | |
4798 | ||
4799 | it("should return a formatter object when a formatter is requested within a scoped npm package", async () => { | |
4800 | const engine = new ESLint({ | |
4801 | cwd: getFixturePath("cli-engine") | |
4802 | }); | |
4803 | const formatter = await engine.loadFormatter("@somenamespace/foo"); | |
4804 | ||
4805 | assert.strictEqual(typeof formatter, "object"); | |
4806 | assert.strictEqual(typeof formatter.format, "function"); | |
4807 | }); | |
4808 | ||
4809 | it("should return a formatter object when a formatter is requested within a scoped npm package, also when the eslint-formatter prefix is included in the format argument", async () => { | |
4810 | const engine = new ESLint({ | |
4811 | cwd: getFixturePath("cli-engine") | |
4812 | }); | |
4813 | const formatter = await engine.loadFormatter("@somenamespace/eslint-formatter-foo"); | |
4814 | ||
4815 | assert.strictEqual(typeof formatter, "object"); | |
4816 | assert.strictEqual(typeof formatter.format, "function"); | |
4817 | }); | |
4818 | ||
609c276f | 4819 | it("should throw if a custom formatter doesn't exist", async () => { |
56c4a2cb DC |
4820 | const engine = new ESLint(); |
4821 | const formatterPath = getFixturePath("formatters", "doesntexist.js"); | |
4822 | const fullFormatterPath = path.resolve(formatterPath); | |
4823 | ||
4824 | await assert.rejects(async () => { | |
4825 | await engine.loadFormatter(formatterPath); | |
4826 | }, new RegExp(escapeStringRegExp(`There was a problem loading formatter: ${fullFormatterPath}\nError: Cannot find module '${fullFormatterPath}'`), "u")); | |
4827 | }); | |
4828 | ||
4829 | it("should throw if a built-in formatter doesn't exist", async () => { | |
4830 | const engine = new ESLint(); | |
4831 | const fullFormatterPath = path.resolve(__dirname, "../../../lib/cli-engine/formatters/special"); | |
4832 | ||
4833 | await assert.rejects(async () => { | |
4834 | await engine.loadFormatter("special"); | |
4835 | }, new RegExp(escapeStringRegExp(`There was a problem loading formatter: ${fullFormatterPath}\nError: Cannot find module '${fullFormatterPath}'`), "u")); | |
4836 | }); | |
4837 | ||
4838 | it("should throw if the required formatter exists but has an error", async () => { | |
4839 | const engine = new ESLint(); | |
4840 | const formatterPath = getFixturePath("formatters", "broken.js"); | |
4841 | ||
4842 | await assert.rejects(async () => { | |
4843 | await engine.loadFormatter(formatterPath); | |
4844 | }, new RegExp(escapeStringRegExp(`There was a problem loading formatter: ${formatterPath}\nError: Cannot find module 'this-module-does-not-exist'`), "u")); | |
4845 | }); | |
4846 | ||
4847 | it("should throw if a non-string formatter name is passed", async () => { | |
4848 | const engine = new ESLint(); | |
4849 | ||
4850 | await assert.rejects(async () => { | |
4851 | await engine.loadFormatter(5); | |
4852 | }, /'name' must be a string/u); | |
4853 | }); | |
34eeec05 TL |
4854 | |
4855 | it("should pass cwd to the `cwd` property of the second argument.", async () => { | |
4856 | const cwd = getFixturePath(); | |
4857 | const engine = new ESLint({ cwd }); | |
4858 | const formatterPath = getFixturePath("formatters", "cwd.js"); | |
4859 | const formatter = await engine.loadFormatter(formatterPath); | |
4860 | ||
4861 | assert.strictEqual(formatter.format([]), cwd); | |
4862 | }); | |
56c4a2cb DC |
4863 | }); |
4864 | ||
4865 | describe("getErrorResults()", () => { | |
4866 | it("should report 5 error messages when looking for errors only", async () => { | |
4867 | process.chdir(originalDir); | |
4868 | const engine = new ESLint(); | |
4869 | const results = await engine.lintText("var foo = 'bar';"); | |
4870 | const errorResults = ESLint.getErrorResults(results); | |
4871 | ||
4872 | assert.strictEqual(errorResults[0].messages.length, 5); | |
4873 | assert.strictEqual(errorResults[0].errorCount, 5); | |
4874 | assert.strictEqual(errorResults[0].fixableErrorCount, 3); | |
4875 | assert.strictEqual(errorResults[0].fixableWarningCount, 0); | |
4876 | assert.strictEqual(errorResults[0].messages[0].ruleId, "strict"); | |
4877 | assert.strictEqual(errorResults[0].messages[0].severity, 2); | |
4878 | assert.strictEqual(errorResults[0].messages[1].ruleId, "no-var"); | |
4879 | assert.strictEqual(errorResults[0].messages[1].severity, 2); | |
4880 | assert.strictEqual(errorResults[0].messages[2].ruleId, "no-unused-vars"); | |
4881 | assert.strictEqual(errorResults[0].messages[2].severity, 2); | |
4882 | assert.strictEqual(errorResults[0].messages[3].ruleId, "quotes"); | |
4883 | assert.strictEqual(errorResults[0].messages[3].severity, 2); | |
4884 | assert.strictEqual(errorResults[0].messages[4].ruleId, "eol-last"); | |
4885 | assert.strictEqual(errorResults[0].messages[4].severity, 2); | |
4886 | }); | |
4887 | ||
4888 | it("should not mutate passed report parameter", async () => { | |
4889 | process.chdir(originalDir); | |
4890 | const engine = new ESLint({ | |
4891 | overrideConfig: { | |
4892 | rules: { quotes: [1, "double"] } | |
4893 | } | |
4894 | }); | |
4895 | const results = await engine.lintText("var foo = 'bar';"); | |
4896 | const reportResultsLength = results[0].messages.length; | |
4897 | ||
4898 | ESLint.getErrorResults(results); | |
4899 | ||
4900 | assert.strictEqual(results[0].messages.length, reportResultsLength); | |
4901 | }); | |
4902 | ||
4903 | it("should report a warningCount of 0 when looking for errors only", async () => { | |
4904 | process.chdir(originalDir); | |
4905 | const engine = new ESLint(); | |
4906 | const results = await engine.lintText("var foo = 'bar';"); | |
4907 | const errorResults = ESLint.getErrorResults(results); | |
4908 | ||
4909 | assert.strictEqual(errorResults[0].warningCount, 0); | |
4910 | assert.strictEqual(errorResults[0].fixableWarningCount, 0); | |
4911 | }); | |
4912 | ||
4913 | it("should return 0 error or warning messages even when the file has warnings", async () => { | |
4914 | const engine = new ESLint({ | |
4915 | ignorePath: path.join(fixtureDir, ".eslintignore"), | |
4916 | cwd: path.join(fixtureDir, "..") | |
4917 | }); | |
4918 | const options = { | |
4919 | filePath: "fixtures/passing.js", | |
4920 | warnIgnored: true | |
4921 | }; | |
4922 | const results = await engine.lintText("var bar = foo;", options); | |
4923 | const errorReport = ESLint.getErrorResults(results); | |
4924 | ||
4925 | assert.strictEqual(errorReport.length, 0); | |
4926 | assert.strictEqual(results.length, 1); | |
4927 | assert.strictEqual(results[0].errorCount, 0); | |
4928 | assert.strictEqual(results[0].warningCount, 1); | |
8f9d1d4d DC |
4929 | assert.strictEqual(results[0].fatalErrorCount, 0); |
4930 | assert.strictEqual(results[0].fixableErrorCount, 0); | |
4931 | assert.strictEqual(results[0].fixableWarningCount, 0); | |
56c4a2cb DC |
4932 | }); |
4933 | ||
4934 | it("should return source code of file in the `source` property", async () => { | |
4935 | process.chdir(originalDir); | |
4936 | const engine = new ESLint({ | |
4937 | useEslintrc: false, | |
4938 | overrideConfig: { | |
4939 | rules: { quotes: [2, "double"] } | |
4940 | } | |
4941 | }); | |
4942 | const results = await engine.lintText("var foo = 'bar';"); | |
4943 | const errorResults = ESLint.getErrorResults(results); | |
4944 | ||
4945 | assert.strictEqual(errorResults[0].messages.length, 1); | |
4946 | assert.strictEqual(errorResults[0].source, "var foo = 'bar';"); | |
4947 | }); | |
4948 | ||
4949 | it("should contain `output` property after fixes", async () => { | |
4950 | process.chdir(originalDir); | |
4951 | const engine = new ESLint({ | |
4952 | useEslintrc: false, | |
4953 | fix: true, | |
4954 | overrideConfig: { | |
4955 | rules: { | |
4956 | semi: 2, | |
4957 | "no-console": 2 | |
4958 | } | |
4959 | } | |
4960 | }); | |
4961 | const results = await engine.lintText("console.log('foo')"); | |
4962 | const errorResults = ESLint.getErrorResults(results); | |
4963 | ||
4964 | assert.strictEqual(errorResults[0].messages.length, 1); | |
4965 | assert.strictEqual(errorResults[0].output, "console.log('foo');"); | |
4966 | }); | |
4967 | }); | |
4968 | ||
609c276f TL |
4969 | describe("getRulesMetaForResults()", () => { |
4970 | it("should return empty object when there are no linting errors", async () => { | |
4971 | const engine = new ESLint({ | |
4972 | useEslintrc: false | |
4973 | }); | |
4974 | ||
4975 | const rulesMeta = engine.getRulesMetaForResults([]); | |
4976 | ||
f2a92ac6 | 4977 | assert.deepStrictEqual(rulesMeta, {}); |
609c276f TL |
4978 | }); |
4979 | ||
4980 | it("should return one rule meta when there is a linting error", async () => { | |
4981 | const engine = new ESLint({ | |
4982 | useEslintrc: false, | |
4983 | overrideConfig: { | |
4984 | rules: { | |
4985 | semi: 2 | |
4986 | } | |
4987 | } | |
4988 | }); | |
4989 | ||
4990 | const results = await engine.lintText("a"); | |
4991 | const rulesMeta = engine.getRulesMetaForResults(results); | |
4992 | ||
f2a92ac6 | 4993 | assert.strictEqual(Object.keys(rulesMeta).length, 1); |
609c276f TL |
4994 | assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); |
4995 | }); | |
4996 | ||
8f9d1d4d DC |
4997 | it("should return one rule meta when there is a suppressed linting error", async () => { |
4998 | const engine = new ESLint({ | |
4999 | useEslintrc: false, | |
5000 | overrideConfig: { | |
5001 | rules: { | |
5002 | semi: 2 | |
5003 | } | |
5004 | } | |
5005 | }); | |
5006 | ||
5007 | const results = await engine.lintText("a // eslint-disable-line semi"); | |
5008 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5009 | ||
f2a92ac6 | 5010 | assert.strictEqual(Object.keys(rulesMeta).length, 1); |
8f9d1d4d DC |
5011 | assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); |
5012 | }); | |
5013 | ||
609c276f TL |
5014 | it("should return multiple rule meta when there are multiple linting errors", async () => { |
5015 | const engine = new ESLint({ | |
5016 | useEslintrc: false, | |
5017 | overrideConfig: { | |
5018 | rules: { | |
5019 | semi: 2, | |
5020 | quotes: [2, "double"] | |
5021 | } | |
5022 | } | |
5023 | }); | |
5024 | ||
5025 | const results = await engine.lintText("'a'"); | |
5026 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5027 | ||
5028 | assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); | |
5029 | assert.strictEqual(rulesMeta.quotes, coreRules.get("quotes").meta); | |
5030 | }); | |
5031 | ||
5032 | it("should return multiple rule meta when there are multiple linting errors from a plugin", async () => { | |
8f9d1d4d | 5033 | const nodePlugin = require("eslint-plugin-n"); |
609c276f TL |
5034 | const engine = new ESLint({ |
5035 | useEslintrc: false, | |
5036 | plugins: { | |
5037 | node: nodePlugin | |
5038 | }, | |
5039 | overrideConfig: { | |
8f9d1d4d | 5040 | plugins: ["n"], |
609c276f | 5041 | rules: { |
8f9d1d4d | 5042 | "n/no-new-require": 2, |
609c276f TL |
5043 | semi: 2, |
5044 | quotes: [2, "double"] | |
5045 | } | |
5046 | } | |
5047 | }); | |
5048 | ||
5049 | const results = await engine.lintText("new require('hi')"); | |
5050 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5051 | ||
5052 | assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); | |
5053 | assert.strictEqual(rulesMeta.quotes, coreRules.get("quotes").meta); | |
5054 | assert.strictEqual( | |
8f9d1d4d | 5055 | rulesMeta["n/no-new-require"], |
609c276f TL |
5056 | nodePlugin.rules["no-new-require"].meta |
5057 | ); | |
5058 | }); | |
f2a92ac6 DC |
5059 | |
5060 | it("should ignore messages not related to a rule", async () => { | |
5061 | const engine = new ESLint({ | |
5062 | useEslintrc: false, | |
5063 | overrideConfig: { | |
5064 | ignorePatterns: "ignored.js", | |
5065 | rules: { | |
5066 | "no-var": "warn" | |
5067 | } | |
5068 | }, | |
5069 | reportUnusedDisableDirectives: "warn" | |
5070 | }); | |
5071 | ||
5072 | { | |
5073 | const results = await engine.lintText("syntax error"); | |
5074 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5075 | ||
5076 | assert.deepStrictEqual(rulesMeta, {}); | |
5077 | } | |
5078 | { | |
5079 | const results = await engine.lintText("// eslint-disable-line no-var"); | |
5080 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5081 | ||
5082 | assert.deepStrictEqual(rulesMeta, {}); | |
5083 | } | |
5084 | { | |
5085 | const results = await engine.lintText("", { filePath: "ignored.js", warnIgnored: true }); | |
5086 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5087 | ||
5088 | assert.deepStrictEqual(rulesMeta, {}); | |
5089 | } | |
5090 | }); | |
5091 | ||
5092 | it("should return a non-empty value if some of the messages are related to a rule", async () => { | |
5093 | const engine = new ESLint({ | |
5094 | useEslintrc: false, | |
5095 | overrideConfig: { rules: { "no-var": "warn" } }, | |
5096 | reportUnusedDisableDirectives: "warn" | |
5097 | }); | |
5098 | ||
5099 | const results = await engine.lintText("// eslint-disable-line no-var\nvar foo;"); | |
5100 | const rulesMeta = engine.getRulesMetaForResults(results); | |
5101 | ||
5102 | assert.deepStrictEqual(rulesMeta, { "no-var": coreRules.get("no-var").meta }); | |
5103 | }); | |
609c276f TL |
5104 | }); |
5105 | ||
56c4a2cb DC |
5106 | describe("outputFixes()", () => { |
5107 | afterEach(() => { | |
5108 | sinon.verifyAndRestore(); | |
5109 | }); | |
5110 | ||
5111 | it("should call fs.writeFile() for each result with output", async () => { | |
6f036462 TL |
5112 | const fakeFS = { |
5113 | writeFile: sinon.spy(callLastArgument) | |
5114 | }; | |
5115 | const spy = fakeFS.writeFile; | |
5116 | const { ESLint: localESLint } = proxyquire("../../../lib/eslint/eslint", { | |
56c4a2cb | 5117 | fs: fakeFS |
6f036462 TL |
5118 | }); |
5119 | ||
56c4a2cb DC |
5120 | const results = [ |
5121 | { | |
5122 | filePath: path.resolve("foo.js"), | |
5123 | output: "bar" | |
5124 | }, | |
5125 | { | |
5126 | filePath: path.resolve("bar.js"), | |
5127 | output: "baz" | |
5128 | } | |
5129 | ]; | |
5130 | ||
5131 | await localESLint.outputFixes(results); | |
5132 | ||
5133 | assert.strictEqual(spy.callCount, 2); | |
5134 | assert(spy.firstCall.calledWithExactly(path.resolve("foo.js"), "bar", sinon.match.func), "First call was incorrect."); | |
5135 | assert(spy.secondCall.calledWithExactly(path.resolve("bar.js"), "baz", sinon.match.func), "Second call was incorrect."); | |
5136 | }); | |
5137 | ||
5138 | it("should call fs.writeFile() for each result with output and not at all for a result without output", async () => { | |
6f036462 TL |
5139 | const fakeFS = { |
5140 | writeFile: sinon.spy(callLastArgument) | |
5141 | }; | |
5142 | const spy = fakeFS.writeFile; | |
5143 | const { ESLint: localESLint } = proxyquire("../../../lib/eslint/eslint", { | |
56c4a2cb | 5144 | fs: fakeFS |
6f036462 | 5145 | }); |
56c4a2cb DC |
5146 | const results = [ |
5147 | { | |
5148 | filePath: path.resolve("foo.js"), | |
5149 | output: "bar" | |
5150 | }, | |
5151 | { | |
5152 | filePath: path.resolve("abc.js") | |
5153 | }, | |
5154 | { | |
5155 | filePath: path.resolve("bar.js"), | |
5156 | output: "baz" | |
5157 | } | |
5158 | ]; | |
5159 | ||
5160 | await localESLint.outputFixes(results); | |
5161 | ||
5162 | assert.strictEqual(spy.callCount, 2); | |
5163 | assert(spy.firstCall.calledWithExactly(path.resolve("foo.js"), "bar", sinon.match.func), "First call was incorrect."); | |
5164 | assert(spy.secondCall.calledWithExactly(path.resolve("bar.js"), "baz", sinon.match.func), "Second call was incorrect."); | |
5165 | }); | |
5166 | ||
5167 | it("should throw if non object array is given to 'results' parameter", async () => { | |
5168 | await assert.rejects(() => ESLint.outputFixes(null), /'results' must be an array/u); | |
5169 | await assert.rejects(() => ESLint.outputFixes([null]), /'results' must include only objects/u); | |
5170 | }); | |
5171 | }); | |
5172 | ||
5173 | describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => { | |
5174 | it("should report a violation for disabling rules", async () => { | |
5175 | const code = [ | |
5176 | "alert('test'); // eslint-disable-line no-alert" | |
5177 | ].join("\n"); | |
5178 | const config = { | |
5179 | ignore: true, | |
609c276f | 5180 | useEslintrc: false, |
56c4a2cb DC |
5181 | allowInlineConfig: false, |
5182 | overrideConfig: { | |
5183 | env: { browser: true }, | |
5184 | rules: { | |
5185 | "eol-last": 0, | |
5186 | "no-alert": 1, | |
5187 | "no-trailing-spaces": 0, | |
5188 | strict: 0, | |
5189 | quotes: 0 | |
5190 | } | |
5191 | } | |
5192 | }; | |
5193 | const eslintCLI = new ESLint(config); | |
5194 | const results = await eslintCLI.lintText(code); | |
5195 | const messages = results[0].messages; | |
5196 | ||
5197 | assert.strictEqual(messages.length, 1); | |
5198 | assert.strictEqual(messages[0].ruleId, "no-alert"); | |
5199 | }); | |
5200 | ||
5201 | it("should not report a violation by default", async () => { | |
5202 | const code = [ | |
5203 | "alert('test'); // eslint-disable-line no-alert" | |
5204 | ].join("\n"); | |
5205 | const config = { | |
5206 | ignore: true, | |
609c276f | 5207 | useEslintrc: false, |
56c4a2cb DC |
5208 | allowInlineConfig: true, |
5209 | overrideConfig: { | |
5210 | env: { browser: true }, | |
5211 | rules: { | |
5212 | "eol-last": 0, | |
5213 | "no-alert": 1, | |
5214 | "no-trailing-spaces": 0, | |
5215 | strict: 0, | |
5216 | quotes: 0 | |
5217 | } | |
5218 | } | |
5219 | }; | |
5220 | const eslintCLI = new ESLint(config); | |
5221 | const results = await eslintCLI.lintText(code); | |
5222 | const messages = results[0].messages; | |
5223 | ||
5224 | assert.strictEqual(messages.length, 0); | |
5225 | }); | |
5226 | }); | |
5227 | ||
5228 | describe("when evaluating code when reportUnusedDisableDirectives is enabled", () => { | |
5229 | it("should report problems for unused eslint-disable directives", async () => { | |
5230 | const eslint = new ESLint({ useEslintrc: false, reportUnusedDisableDirectives: "error" }); | |
5231 | ||
5232 | assert.deepStrictEqual( | |
5233 | await eslint.lintText("/* eslint-disable */"), | |
5234 | [ | |
5235 | { | |
5236 | filePath: "<text>", | |
5237 | messages: [ | |
5238 | { | |
5239 | ruleId: null, | |
5240 | message: "Unused eslint-disable directive (no problems were reported).", | |
5241 | line: 1, | |
5242 | column: 1, | |
609c276f TL |
5243 | fix: { |
5244 | range: [0, 20], | |
5245 | text: " " | |
5246 | }, | |
56c4a2cb DC |
5247 | severity: 2, |
5248 | nodeType: null | |
5249 | } | |
5250 | ], | |
8f9d1d4d | 5251 | suppressedMessages: [], |
56c4a2cb DC |
5252 | errorCount: 1, |
5253 | warningCount: 0, | |
609c276f TL |
5254 | fatalErrorCount: 0, |
5255 | fixableErrorCount: 1, | |
56c4a2cb DC |
5256 | fixableWarningCount: 0, |
5257 | source: "/* eslint-disable */", | |
5258 | usedDeprecatedRules: [] | |
5259 | } | |
5260 | ] | |
5261 | ); | |
5262 | }); | |
5263 | }); | |
5264 | ||
8f9d1d4d | 5265 | describe("when retrieving version number", () => { |
56c4a2cb DC |
5266 | it("should return current version number", () => { |
5267 | const eslintCLI = require("../../../lib/eslint").ESLint; | |
5268 | const version = eslintCLI.version; | |
5269 | ||
5270 | assert.strictEqual(typeof version, "string"); | |
5271 | assert(parseInt(version[0], 10) >= 3); | |
5272 | }); | |
5273 | }); | |
5274 | ||
5275 | describe("mutability", () => { | |
5276 | describe("plugins", () => { | |
5277 | it("Loading plugin in one instance doesn't mutate to another instance", async () => { | |
5278 | const filePath = getFixturePath("single-quoted.js"); | |
5279 | const engine1 = eslintWithPlugins({ | |
5280 | cwd: path.join(fixtureDir, ".."), | |
5281 | useEslintrc: false, | |
5282 | overrideConfig: { | |
5283 | plugins: ["example"], | |
5284 | rules: { "example/example-rule": 1 } | |
5285 | } | |
5286 | }); | |
5287 | const engine2 = new ESLint({ | |
5288 | cwd: path.join(fixtureDir, ".."), | |
5289 | useEslintrc: false | |
5290 | }); | |
5291 | const fileConfig1 = await engine1.calculateConfigForFile(filePath); | |
5292 | const fileConfig2 = await engine2.calculateConfigForFile(filePath); | |
5293 | ||
5294 | // plugin | |
5295 | assert.deepStrictEqual(fileConfig1.plugins, ["example"], "Plugin is present for engine 1"); | |
5296 | assert.deepStrictEqual(fileConfig2.plugins, [], "Plugin is not present for engine 2"); | |
5297 | }); | |
5298 | }); | |
5299 | ||
5300 | describe("rules", () => { | |
5301 | it("Loading rules in one instance doesn't mutate to another instance", async () => { | |
5302 | const filePath = getFixturePath("single-quoted.js"); | |
5303 | const engine1 = new ESLint({ | |
5304 | cwd: path.join(fixtureDir, ".."), | |
5305 | useEslintrc: false, | |
5306 | overrideConfig: { rules: { "example/example-rule": 1 } } | |
5307 | }); | |
5308 | const engine2 = new ESLint({ | |
5309 | cwd: path.join(fixtureDir, ".."), | |
5310 | useEslintrc: false | |
5311 | }); | |
5312 | const fileConfig1 = await engine1.calculateConfigForFile(filePath); | |
5313 | const fileConfig2 = await engine2.calculateConfigForFile(filePath); | |
5314 | ||
5315 | // plugin | |
5316 | assert.deepStrictEqual(fileConfig1.rules["example/example-rule"], [1], "example is present for engine 1"); | |
5317 | assert.strictEqual(fileConfig2.rules["example/example-rule"], void 0, "example is not present for engine 2"); | |
5318 | }); | |
5319 | }); | |
5320 | }); | |
5321 | ||
5322 | describe("with ignorePatterns config", () => { | |
5323 | const root = getFixturePath("cli-engine/ignore-patterns"); | |
5324 | ||
56c4a2cb | 5325 | describe("ignorePatterns can add an ignore pattern ('foo.js').", () => { |
6f036462 TL |
5326 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5327 | cwd: root, | |
5328 | files: { | |
5329 | ".eslintrc.json": { | |
5330 | ignorePatterns: "foo.js" | |
5331 | }, | |
5332 | "foo.js": "", | |
5333 | "bar.js": "", | |
5334 | "subdir/foo.js": "", | |
5335 | "subdir/bar.js": "" | |
5336 | } | |
56c4a2cb DC |
5337 | }); |
5338 | ||
6f036462 TL |
5339 | beforeEach(prepare); |
5340 | afterEach(cleanup); | |
5341 | ||
56c4a2cb | 5342 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5343 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5344 | |
5345 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5346 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); | |
5347 | }); | |
5348 | ||
5349 | it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { | |
6f036462 | 5350 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5351 | |
5352 | assert.strictEqual(await engine.isPathIgnored("bar.js"), false); | |
5353 | assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), false); | |
5354 | }); | |
5355 | ||
5356 | it("'lintFiles()' should not verify 'foo.js'.", async () => { | |
6f036462 | 5357 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5358 | const filePaths = (await engine.lintFiles("**/*.js")) |
5359 | .map(r => r.filePath) | |
5360 | .sort(); | |
5361 | ||
5362 | assert.deepStrictEqual(filePaths, [ | |
5363 | path.join(root, "bar.js"), | |
5364 | path.join(root, "subdir/bar.js") | |
5365 | ]); | |
5366 | }); | |
5367 | }); | |
5368 | ||
5369 | describe("ignorePatterns can add ignore patterns ('foo.js', '/bar.js').", () => { | |
6f036462 TL |
5370 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5371 | cwd: root, | |
5372 | files: { | |
5373 | ".eslintrc.json": { | |
5374 | ignorePatterns: ["foo.js", "/bar.js"] | |
5375 | }, | |
5376 | "foo.js": "", | |
5377 | "bar.js": "", | |
5378 | "baz.js": "", | |
5379 | "subdir/foo.js": "", | |
5380 | "subdir/bar.js": "", | |
5381 | "subdir/baz.js": "" | |
5382 | } | |
56c4a2cb DC |
5383 | }); |
5384 | ||
6f036462 TL |
5385 | beforeEach(prepare); |
5386 | afterEach(cleanup); | |
5387 | ||
56c4a2cb | 5388 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5389 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5390 | |
5391 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5392 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); | |
5393 | }); | |
5394 | ||
5395 | it("'isPathIgnored()' should return 'true' for '/bar.js'.", async () => { | |
6f036462 | 5396 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5397 | |
5398 | assert.strictEqual(await engine.isPathIgnored("bar.js"), true); | |
5399 | assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), false); | |
5400 | }); | |
5401 | ||
5402 | it("'lintFiles()' should not verify 'foo.js' and '/bar.js'.", async () => { | |
6f036462 | 5403 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5404 | const filePaths = (await engine.lintFiles("**/*.js")) |
5405 | .map(r => r.filePath) | |
5406 | .sort(); | |
5407 | ||
5408 | assert.deepStrictEqual(filePaths, [ | |
5409 | path.join(root, "baz.js"), | |
5410 | path.join(root, "subdir/bar.js"), | |
5411 | path.join(root, "subdir/baz.js") | |
5412 | ]); | |
5413 | }); | |
5414 | }); | |
5415 | ||
5416 | describe("ignorePatterns can unignore '/node_modules/foo'.", () => { | |
6f036462 TL |
5417 | |
5418 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
5419 | cwd: root, | |
5420 | files: { | |
5421 | ".eslintrc.json": { | |
5422 | ignorePatterns: "!/node_modules/foo" | |
5423 | }, | |
5424 | "node_modules/foo/index.js": "", | |
5425 | "node_modules/foo/.dot.js": "", | |
5426 | "node_modules/bar/index.js": "", | |
5427 | "foo.js": "" | |
5428 | } | |
56c4a2cb DC |
5429 | }); |
5430 | ||
6f036462 TL |
5431 | beforeEach(prepare); |
5432 | afterEach(cleanup); | |
5433 | ||
56c4a2cb | 5434 | it("'isPathIgnored()' should return 'false' for 'node_modules/foo/index.js'.", async () => { |
6f036462 | 5435 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5436 | |
5437 | assert.strictEqual(await engine.isPathIgnored("node_modules/foo/index.js"), false); | |
5438 | }); | |
5439 | ||
5440 | it("'isPathIgnored()' should return 'true' for 'node_modules/foo/.dot.js'.", async () => { | |
6f036462 | 5441 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5442 | |
5443 | assert.strictEqual(await engine.isPathIgnored("node_modules/foo/.dot.js"), true); | |
5444 | }); | |
5445 | ||
5446 | it("'isPathIgnored()' should return 'true' for 'node_modules/bar/index.js'.", async () => { | |
6f036462 | 5447 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5448 | |
5449 | assert.strictEqual(await engine.isPathIgnored("node_modules/bar/index.js"), true); | |
5450 | }); | |
5451 | ||
5452 | it("'lintFiles()' should verify 'node_modules/foo/index.js'.", async () => { | |
6f036462 | 5453 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5454 | const filePaths = (await engine.lintFiles("**/*.js")) |
5455 | .map(r => r.filePath) | |
5456 | .sort(); | |
5457 | ||
5458 | assert.deepStrictEqual(filePaths, [ | |
5459 | path.join(root, "foo.js"), | |
5460 | path.join(root, "node_modules/foo/index.js") | |
5461 | ]); | |
5462 | }); | |
5463 | }); | |
5464 | ||
5465 | describe("ignorePatterns can unignore '.eslintrc.js'.", () => { | |
6f036462 TL |
5466 | |
5467 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
5468 | cwd: root, | |
5469 | files: { | |
5470 | ".eslintrc.js": `module.exports = ${JSON.stringify({ | |
5471 | ignorePatterns: "!.eslintrc.js" | |
5472 | })}`, | |
5473 | "foo.js": "" | |
5474 | } | |
56c4a2cb DC |
5475 | }); |
5476 | ||
6f036462 TL |
5477 | beforeEach(prepare); |
5478 | afterEach(cleanup); | |
5479 | ||
56c4a2cb | 5480 | it("'isPathIgnored()' should return 'false' for '.eslintrc.js'.", async () => { |
6f036462 | 5481 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5482 | |
5483 | assert.strictEqual(await engine.isPathIgnored(".eslintrc.js"), false); | |
5484 | }); | |
5485 | ||
5486 | it("'lintFiles()' should verify '.eslintrc.js'.", async () => { | |
6f036462 | 5487 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5488 | const filePaths = (await engine.lintFiles("**/*.js")) |
5489 | .map(r => r.filePath) | |
5490 | .sort(); | |
5491 | ||
5492 | assert.deepStrictEqual(filePaths, [ | |
5493 | path.join(root, ".eslintrc.js"), | |
5494 | path.join(root, "foo.js") | |
5495 | ]); | |
5496 | }); | |
5497 | }); | |
5498 | ||
5499 | describe(".eslintignore can re-ignore files that are unignored by ignorePatterns.", () => { | |
6f036462 TL |
5500 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5501 | cwd: root, | |
5502 | files: { | |
5503 | ".eslintrc.js": `module.exports = ${JSON.stringify({ | |
5504 | ignorePatterns: "!.*" | |
5505 | })}`, | |
5506 | ".eslintignore": ".foo*", | |
5507 | ".foo.js": "", | |
5508 | ".bar.js": "" | |
5509 | } | |
56c4a2cb DC |
5510 | }); |
5511 | ||
6f036462 TL |
5512 | beforeEach(prepare); |
5513 | afterEach(cleanup); | |
5514 | ||
56c4a2cb | 5515 | it("'isPathIgnored()' should return 'true' for re-ignored '.foo.js'.", async () => { |
6f036462 | 5516 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5517 | |
5518 | assert.strictEqual(await engine.isPathIgnored(".foo.js"), true); | |
5519 | }); | |
5520 | ||
5521 | it("'isPathIgnored()' should return 'false' for unignored '.bar.js'.", async () => { | |
6f036462 | 5522 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5523 | |
5524 | assert.strictEqual(await engine.isPathIgnored(".bar.js"), false); | |
5525 | }); | |
5526 | ||
5527 | it("'lintFiles()' should not verify re-ignored '.foo.js'.", async () => { | |
6f036462 | 5528 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5529 | const filePaths = (await engine.lintFiles("**/*.js")) |
5530 | .map(r => r.filePath) | |
5531 | .sort(); | |
5532 | ||
5533 | assert.deepStrictEqual(filePaths, [ | |
5534 | path.join(root, ".bar.js"), | |
5535 | path.join(root, ".eslintrc.js") | |
5536 | ]); | |
5537 | }); | |
5538 | }); | |
5539 | ||
5540 | describe(".eslintignore can unignore files that are ignored by ignorePatterns.", () => { | |
6f036462 TL |
5541 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5542 | cwd: root, | |
5543 | files: { | |
5544 | ".eslintrc.js": `module.exports = ${JSON.stringify({ | |
5545 | ignorePatterns: "*.js" | |
5546 | })}`, | |
5547 | ".eslintignore": "!foo.js", | |
5548 | "foo.js": "", | |
5549 | "bar.js": "" | |
5550 | } | |
56c4a2cb DC |
5551 | }); |
5552 | ||
6f036462 TL |
5553 | beforeEach(prepare); |
5554 | afterEach(cleanup); | |
5555 | ||
56c4a2cb | 5556 | it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", async () => { |
6f036462 | 5557 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5558 | |
5559 | assert.strictEqual(await engine.isPathIgnored("foo.js"), false); | |
5560 | }); | |
5561 | ||
5562 | it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", async () => { | |
6f036462 | 5563 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5564 | |
5565 | assert.strictEqual(await engine.isPathIgnored("bar.js"), true); | |
5566 | }); | |
5567 | ||
5568 | it("'lintFiles()' should verify unignored 'foo.js'.", async () => { | |
6f036462 | 5569 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5570 | const filePaths = (await engine.lintFiles("**/*.js")) |
5571 | .map(r => r.filePath) | |
5572 | .sort(); | |
5573 | ||
5574 | assert.deepStrictEqual(filePaths, [ | |
5575 | path.join(root, "foo.js") | |
5576 | ]); | |
5577 | }); | |
5578 | }); | |
5579 | ||
5580 | describe("ignorePatterns in the config file in a child directory affects to only in the directory.", () => { | |
6f036462 TL |
5581 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5582 | cwd: root, | |
5583 | files: { | |
5584 | ".eslintrc.json": JSON.stringify({ | |
5585 | ignorePatterns: "foo.js" | |
5586 | }), | |
5587 | "subdir/.eslintrc.json": JSON.stringify({ | |
5588 | ignorePatterns: "bar.js" | |
5589 | }), | |
5590 | "foo.js": "", | |
5591 | "bar.js": "", | |
5592 | "subdir/foo.js": "", | |
5593 | "subdir/bar.js": "", | |
5594 | "subdir/subsubdir/foo.js": "", | |
5595 | "subdir/subsubdir/bar.js": "" | |
5596 | } | |
56c4a2cb DC |
5597 | }); |
5598 | ||
6f036462 TL |
5599 | |
5600 | beforeEach(prepare); | |
5601 | afterEach(cleanup); | |
5602 | ||
56c4a2cb | 5603 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5604 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5605 | |
5606 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5607 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); | |
5608 | assert.strictEqual(await engine.isPathIgnored("subdir/subsubdir/foo.js"), true); | |
5609 | }); | |
5610 | ||
5611 | it("'isPathIgnored()' should return 'true' for 'bar.js' in 'subdir'.", async () => { | |
6f036462 | 5612 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5613 | |
5614 | assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); | |
5615 | assert.strictEqual(await engine.isPathIgnored("subdir/subsubdir/bar.js"), true); | |
5616 | }); | |
5617 | ||
5618 | it("'isPathIgnored()' should return 'false' for 'bar.js' in the outside of 'subdir'.", async () => { | |
6f036462 | 5619 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5620 | |
5621 | assert.strictEqual(await engine.isPathIgnored("bar.js"), false); | |
5622 | }); | |
5623 | ||
5624 | it("'lintFiles()' should verify 'bar.js' in the outside of 'subdir'.", async () => { | |
6f036462 | 5625 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5626 | const filePaths = (await engine.lintFiles("**/*.js")) |
5627 | .map(r => r.filePath) | |
5628 | .sort(); | |
5629 | ||
5630 | assert.deepStrictEqual(filePaths, [ | |
5631 | path.join(root, "bar.js") | |
5632 | ]); | |
5633 | }); | |
5634 | }); | |
5635 | ||
5636 | describe("ignorePatterns in the config file in a child directory can unignore the ignored files in the parent directory's config.", () => { | |
6f036462 TL |
5637 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5638 | cwd: root, | |
5639 | files: { | |
5640 | ".eslintrc.json": JSON.stringify({ | |
5641 | ignorePatterns: "foo.js" | |
5642 | }), | |
5643 | "subdir/.eslintrc.json": JSON.stringify({ | |
5644 | ignorePatterns: "!foo.js" | |
5645 | }), | |
5646 | "foo.js": "", | |
5647 | "subdir/foo.js": "" | |
5648 | } | |
56c4a2cb DC |
5649 | }); |
5650 | ||
6f036462 TL |
5651 | beforeEach(prepare); |
5652 | afterEach(cleanup); | |
5653 | ||
56c4a2cb | 5654 | it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", async () => { |
6f036462 | 5655 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5656 | |
5657 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5658 | }); | |
5659 | ||
5660 | it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", async () => { | |
6f036462 | 5661 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5662 | |
5663 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); | |
5664 | }); | |
5665 | ||
5666 | it("'lintFiles()' should verify 'foo.js' in the child directory.", async () => { | |
6f036462 | 5667 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5668 | const filePaths = (await engine.lintFiles("**/*.js")) |
5669 | .map(r => r.filePath) | |
5670 | .sort(); | |
5671 | ||
5672 | assert.deepStrictEqual(filePaths, [ | |
5673 | path.join(root, "subdir/foo.js") | |
5674 | ]); | |
5675 | }); | |
5676 | }); | |
5677 | ||
5678 | describe(".eslintignore can unignore files that are ignored by ignorePatterns in the config file in the child directory.", () => { | |
6f036462 TL |
5679 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5680 | cwd: root, | |
5681 | files: { | |
5682 | ".eslintrc.json": JSON.stringify({}), | |
5683 | "subdir/.eslintrc.json": JSON.stringify({ | |
5684 | ignorePatterns: "*.js" | |
5685 | }), | |
5686 | ".eslintignore": "!foo.js", | |
5687 | "foo.js": "", | |
5688 | "subdir/foo.js": "", | |
5689 | "subdir/bar.js": "" | |
5690 | } | |
56c4a2cb DC |
5691 | }); |
5692 | ||
6f036462 TL |
5693 | beforeEach(prepare); |
5694 | afterEach(cleanup); | |
5695 | ||
56c4a2cb | 5696 | it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", async () => { |
6f036462 | 5697 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5698 | |
5699 | assert.strictEqual(await engine.isPathIgnored("foo.js"), false); | |
5700 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); | |
5701 | }); | |
5702 | ||
5703 | it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", async () => { | |
6f036462 | 5704 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5705 | |
5706 | assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); | |
5707 | }); | |
5708 | ||
5709 | it("'lintFiles()' should verify unignored 'foo.js'.", async () => { | |
6f036462 | 5710 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5711 | const filePaths = (await engine.lintFiles("**/*.js")) |
5712 | .map(r => r.filePath) | |
5713 | .sort(); | |
5714 | ||
5715 | assert.deepStrictEqual(filePaths, [ | |
5716 | path.join(root, "foo.js"), | |
5717 | path.join(root, "subdir/foo.js") | |
5718 | ]); | |
5719 | }); | |
5720 | }); | |
5721 | ||
5722 | describe("if the config in a child directory has 'root:true', ignorePatterns in the config file in the parent directory should not be used.", () => { | |
6f036462 TL |
5723 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5724 | cwd: root, | |
5725 | files: { | |
5726 | ".eslintrc.json": JSON.stringify({ | |
5727 | ignorePatterns: "foo.js" | |
5728 | }), | |
5729 | "subdir/.eslintrc.json": JSON.stringify({ | |
5730 | root: true, | |
5731 | ignorePatterns: "bar.js" | |
5732 | }), | |
5733 | "foo.js": "", | |
5734 | "bar.js": "", | |
5735 | "subdir/foo.js": "", | |
5736 | "subdir/bar.js": "" | |
5737 | } | |
56c4a2cb DC |
5738 | }); |
5739 | ||
6f036462 TL |
5740 | beforeEach(prepare); |
5741 | afterEach(cleanup); | |
5742 | ||
56c4a2cb | 5743 | it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", async () => { |
6f036462 | 5744 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5745 | |
5746 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5747 | }); | |
5748 | ||
5749 | it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", async () => { | |
6f036462 | 5750 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5751 | |
5752 | assert.strictEqual(await engine.isPathIgnored("bar.js"), false); | |
5753 | }); | |
5754 | ||
5755 | it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", async () => { | |
6f036462 | 5756 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5757 | |
5758 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); | |
5759 | }); | |
5760 | ||
5761 | it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", async () => { | |
6f036462 | 5762 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5763 | |
5764 | assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); | |
5765 | }); | |
5766 | ||
5767 | it("'lintFiles()' should verify 'bar.js' in the root directory and 'foo.js' in the child directory.", async () => { | |
6f036462 | 5768 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5769 | const filePaths = (await engine.lintFiles("**/*.js")) |
5770 | .map(r => r.filePath) | |
5771 | .sort(); | |
5772 | ||
5773 | assert.deepStrictEqual(filePaths, [ | |
5774 | path.join(root, "bar.js"), | |
5775 | path.join(root, "subdir/foo.js") | |
5776 | ]); | |
5777 | }); | |
5778 | }); | |
5779 | ||
5780 | describe("even if the config in a child directory has 'root:true', .eslintignore should be used.", () => { | |
6f036462 TL |
5781 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5782 | cwd: root, | |
5783 | files: { | |
5784 | ".eslintrc.json": JSON.stringify({}), | |
5785 | "subdir/.eslintrc.json": JSON.stringify({ | |
5786 | root: true, | |
5787 | ignorePatterns: "bar.js" | |
5788 | }), | |
5789 | ".eslintignore": "foo.js", | |
5790 | "foo.js": "", | |
5791 | "bar.js": "", | |
5792 | "subdir/foo.js": "", | |
5793 | "subdir/bar.js": "" | |
5794 | } | |
56c4a2cb DC |
5795 | }); |
5796 | ||
6f036462 TL |
5797 | beforeEach(prepare); |
5798 | afterEach(cleanup); | |
5799 | ||
56c4a2cb | 5800 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5801 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5802 | |
5803 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5804 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); | |
5805 | }); | |
5806 | ||
5807 | it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", async () => { | |
6f036462 | 5808 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5809 | |
5810 | assert.strictEqual(await engine.isPathIgnored("bar.js"), false); | |
5811 | }); | |
5812 | ||
5813 | it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", async () => { | |
6f036462 | 5814 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5815 | |
5816 | assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); | |
5817 | }); | |
5818 | ||
5819 | it("'lintFiles()' should verify 'bar.js' in the root directory.", async () => { | |
6f036462 | 5820 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5821 | const filePaths = (await engine.lintFiles("**/*.js")) |
5822 | .map(r => r.filePath) | |
5823 | .sort(); | |
5824 | ||
5825 | assert.deepStrictEqual(filePaths, [ | |
5826 | path.join(root, "bar.js") | |
5827 | ]); | |
5828 | }); | |
5829 | }); | |
5830 | ||
5831 | describe("ignorePatterns in the shareable config should be used.", () => { | |
6f036462 TL |
5832 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5833 | cwd: root, | |
5834 | files: { | |
5835 | "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ | |
5836 | ignorePatterns: "foo.js" | |
5837 | })}`, | |
5838 | ".eslintrc.json": JSON.stringify({ | |
5839 | extends: "one" | |
5840 | }), | |
5841 | "foo.js": "", | |
5842 | "bar.js": "" | |
5843 | } | |
56c4a2cb DC |
5844 | }); |
5845 | ||
6f036462 TL |
5846 | beforeEach(prepare); |
5847 | afterEach(cleanup); | |
5848 | ||
56c4a2cb | 5849 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5850 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5851 | |
5852 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5853 | }); | |
5854 | ||
5855 | it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { | |
6f036462 | 5856 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5857 | |
5858 | assert.strictEqual(await engine.isPathIgnored("bar.js"), false); | |
5859 | }); | |
5860 | ||
5861 | it("'lintFiles()' should verify 'bar.js'.", async () => { | |
6f036462 | 5862 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5863 | const filePaths = (await engine.lintFiles("**/*.js")) |
5864 | .map(r => r.filePath) | |
5865 | .sort(); | |
5866 | ||
5867 | assert.deepStrictEqual(filePaths, [ | |
5868 | path.join(root, "bar.js") | |
5869 | ]); | |
5870 | }); | |
5871 | }); | |
5872 | ||
5873 | describe("ignorePatterns in the shareable config should be relative to the entry config file.", () => { | |
6f036462 TL |
5874 | |
5875 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
5876 | cwd: root, | |
5877 | files: { | |
5878 | "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ | |
5879 | ignorePatterns: "/foo.js" | |
5880 | })}`, | |
5881 | ".eslintrc.json": JSON.stringify({ | |
5882 | extends: "one" | |
5883 | }), | |
5884 | "foo.js": "", | |
5885 | "subdir/foo.js": "" | |
5886 | } | |
56c4a2cb DC |
5887 | }); |
5888 | ||
6f036462 TL |
5889 | beforeEach(prepare); |
5890 | afterEach(cleanup); | |
5891 | ||
56c4a2cb | 5892 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5893 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5894 | |
5895 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5896 | }); | |
5897 | ||
5898 | it("'isPathIgnored()' should return 'false' for 'subdir/foo.js'.", async () => { | |
6f036462 | 5899 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5900 | |
5901 | assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); | |
5902 | }); | |
5903 | ||
5904 | it("'lintFiles()' should verify 'subdir/foo.js'.", async () => { | |
6f036462 | 5905 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5906 | const filePaths = (await engine.lintFiles("**/*.js")) |
5907 | .map(r => r.filePath) | |
5908 | .sort(); | |
5909 | ||
5910 | assert.deepStrictEqual(filePaths, [ | |
5911 | path.join(root, "subdir/foo.js") | |
5912 | ]); | |
5913 | }); | |
5914 | }); | |
5915 | ||
5916 | describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config.", () => { | |
6f036462 TL |
5917 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
5918 | cwd: root, | |
5919 | files: { | |
5920 | "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ | |
5921 | ignorePatterns: "*.js" | |
5922 | })}`, | |
5923 | ".eslintrc.json": JSON.stringify({ | |
5924 | extends: "one", | |
5925 | ignorePatterns: "!bar.js" | |
5926 | }), | |
5927 | "foo.js": "", | |
5928 | "bar.js": "" | |
5929 | } | |
56c4a2cb DC |
5930 | }); |
5931 | ||
6f036462 TL |
5932 | beforeEach(prepare); |
5933 | afterEach(cleanup); | |
5934 | ||
56c4a2cb | 5935 | it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { |
6f036462 | 5936 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5937 | |
5938 | assert.strictEqual(await engine.isPathIgnored("foo.js"), true); | |
5939 | }); | |
5940 | ||
5941 | it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { | |
6f036462 | 5942 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5943 | |
5944 | assert.strictEqual(await engine.isPathIgnored("bar.js"), false); | |
5945 | }); | |
5946 | ||
5947 | it("'lintFiles()' should verify 'bar.js'.", async () => { | |
6f036462 | 5948 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
5949 | const filePaths = (await engine.lintFiles("**/*.js")) |
5950 | .map(r => r.filePath) | |
5951 | .sort(); | |
5952 | ||
5953 | assert.deepStrictEqual(filePaths, [ | |
5954 | path.join(root, "bar.js") | |
5955 | ]); | |
5956 | }); | |
5957 | }); | |
5958 | ||
5959 | describe("ignorePatterns in a config file should not be used if --no-ignore option was given.", () => { | |
6f036462 TL |
5960 | |
5961 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
5962 | cwd: root, | |
5963 | files: { | |
5964 | ".eslintrc.json": JSON.stringify({ | |
5965 | ignorePatterns: "*.js" | |
5966 | }), | |
5967 | "foo.js": "" | |
5968 | } | |
56c4a2cb DC |
5969 | }); |
5970 | ||
6f036462 TL |
5971 | beforeEach(prepare); |
5972 | afterEach(cleanup); | |
5973 | ||
56c4a2cb | 5974 | it("'isPathIgnored()' should return 'false' for 'foo.js'.", async () => { |
6f036462 | 5975 | const engine = new ESLint({ cwd: getPath(), ignore: false }); |
56c4a2cb DC |
5976 | |
5977 | assert.strictEqual(await engine.isPathIgnored("foo.js"), false); | |
5978 | }); | |
5979 | ||
5980 | it("'lintFiles()' should verify 'foo.js'.", async () => { | |
6f036462 | 5981 | const engine = new ESLint({ cwd: getPath(), ignore: false }); |
56c4a2cb DC |
5982 | const filePaths = (await engine.lintFiles("**/*.js")) |
5983 | .map(r => r.filePath) | |
5984 | .sort(); | |
5985 | ||
5986 | assert.deepStrictEqual(filePaths, [ | |
5987 | path.join(root, "foo.js") | |
5988 | ]); | |
5989 | }); | |
5990 | }); | |
5991 | ||
5992 | describe("ignorePatterns in overrides section is not allowed.", () => { | |
6f036462 TL |
5993 | |
5994 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
5995 | cwd: root, | |
5996 | files: { | |
5997 | ".eslintrc.js": `module.exports = ${JSON.stringify({ | |
5998 | overrides: [ | |
5999 | { | |
6000 | files: "*.js", | |
6001 | ignorePatterns: "foo.js" | |
6002 | } | |
6003 | ] | |
6004 | })}`, | |
6005 | "foo.js": "" | |
6006 | } | |
56c4a2cb DC |
6007 | }); |
6008 | ||
6f036462 TL |
6009 | beforeEach(prepare); |
6010 | afterEach(cleanup); | |
6011 | ||
56c4a2cb DC |
6012 | it("should throw a configuration error.", async () => { |
6013 | await assert.rejects(async () => { | |
6f036462 | 6014 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6015 | |
6016 | await engine.lintFiles("*.js"); | |
6017 | }, /Unexpected top-level property "overrides\[0\]\.ignorePatterns"/u); | |
6018 | }); | |
6019 | }); | |
6020 | }); | |
6021 | ||
6022 | describe("'overrides[].files' adds lint targets", () => { | |
6023 | const root = getFixturePath("cli-engine/additional-lint-targets"); | |
6f036462 | 6024 | |
56c4a2cb DC |
6025 | |
6026 | describe("if { files: 'foo/*.txt', excludedFiles: '**/ignore.txt' } is present,", () => { | |
6f036462 TL |
6027 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
6028 | cwd: root, | |
6029 | files: { | |
6030 | ".eslintrc.json": JSON.stringify({ | |
6031 | overrides: [ | |
6032 | { | |
6033 | files: "foo/*.txt", | |
6034 | excludedFiles: "**/ignore.txt" | |
6035 | } | |
6036 | ] | |
6037 | }), | |
6038 | "foo/nested/test.txt": "", | |
6039 | "foo/test.js": "", | |
6040 | "foo/test.txt": "", | |
6041 | "foo/ignore.txt": "", | |
6042 | "bar/test.js": "", | |
6043 | "bar/test.txt": "", | |
6044 | "bar/ignore.txt": "", | |
6045 | "test.js": "", | |
6046 | "test.txt": "", | |
6047 | "ignore.txt": "" | |
6048 | } | |
56c4a2cb DC |
6049 | }); |
6050 | ||
6f036462 TL |
6051 | beforeEach(prepare); |
6052 | afterEach(cleanup); | |
6053 | ||
56c4a2cb | 6054 | it("'lintFiles()' with a directory path should contain 'foo/test.txt'.", async () => { |
6f036462 | 6055 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6056 | const filePaths = (await engine.lintFiles(".")) |
6057 | .map(r => r.filePath) | |
6058 | .sort(); | |
6059 | ||
6060 | assert.deepStrictEqual(filePaths, [ | |
6061 | path.join(root, "bar/test.js"), | |
6062 | path.join(root, "foo/test.js"), | |
6063 | path.join(root, "foo/test.txt"), | |
6064 | path.join(root, "test.js") | |
6065 | ]); | |
6066 | }); | |
6067 | ||
6068 | it("'lintFiles()' with a glob pattern '*.js' should not contain 'foo/test.txt'.", async () => { | |
6f036462 | 6069 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6070 | const filePaths = (await engine.lintFiles("**/*.js")) |
6071 | .map(r => r.filePath) | |
6072 | .sort(); | |
6073 | ||
6074 | assert.deepStrictEqual(filePaths, [ | |
6075 | path.join(root, "bar/test.js"), | |
6076 | path.join(root, "foo/test.js"), | |
6077 | path.join(root, "test.js") | |
6078 | ]); | |
6079 | }); | |
6080 | }); | |
6081 | ||
6082 | describe("if { files: 'foo/**/*.txt' } is present,", () => { | |
6f036462 TL |
6083 | |
6084 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6085 | cwd: root, | |
6086 | files: { | |
6087 | ".eslintrc.json": JSON.stringify({ | |
6088 | overrides: [ | |
6089 | { | |
6090 | files: "foo/**/*.txt" | |
6091 | } | |
6092 | ] | |
6093 | }), | |
6094 | "foo/nested/test.txt": "", | |
6095 | "foo/test.js": "", | |
6096 | "foo/test.txt": "", | |
6097 | "bar/test.js": "", | |
6098 | "bar/test.txt": "", | |
6099 | "test.js": "", | |
6100 | "test.txt": "" | |
6101 | } | |
56c4a2cb DC |
6102 | }); |
6103 | ||
6f036462 TL |
6104 | beforeEach(prepare); |
6105 | afterEach(cleanup); | |
6106 | ||
56c4a2cb | 6107 | it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { |
6f036462 | 6108 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6109 | const filePaths = (await engine.lintFiles(".")) |
6110 | .map(r => r.filePath) | |
6111 | .sort(); | |
6112 | ||
6113 | assert.deepStrictEqual(filePaths, [ | |
6114 | path.join(root, "bar/test.js"), | |
6115 | path.join(root, "foo/nested/test.txt"), | |
6116 | path.join(root, "foo/test.js"), | |
6117 | path.join(root, "foo/test.txt"), | |
6118 | path.join(root, "test.js") | |
6119 | ]); | |
6120 | }); | |
6121 | }); | |
6122 | ||
6123 | describe("if { files: 'foo/**/*' } is present,", () => { | |
6f036462 TL |
6124 | |
6125 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6126 | cwd: root, | |
6127 | files: { | |
6128 | ".eslintrc.json": JSON.stringify({ | |
6129 | overrides: [ | |
6130 | { | |
6131 | files: "foo/**/*" | |
6132 | } | |
6133 | ] | |
6134 | }), | |
6135 | "foo/nested/test.txt": "", | |
6136 | "foo/test.js": "", | |
6137 | "foo/test.txt": "", | |
6138 | "bar/test.js": "", | |
6139 | "bar/test.txt": "", | |
6140 | "test.js": "", | |
6141 | "test.txt": "" | |
6142 | } | |
56c4a2cb DC |
6143 | }); |
6144 | ||
6f036462 TL |
6145 | beforeEach(prepare); |
6146 | afterEach(cleanup); | |
6147 | ||
56c4a2cb | 6148 | it("'lintFiles()' with a directory path should NOT contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { |
6f036462 | 6149 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6150 | const filePaths = (await engine.lintFiles(".")) |
6151 | .map(r => r.filePath) | |
6152 | .sort(); | |
6153 | ||
6154 | assert.deepStrictEqual(filePaths, [ | |
6155 | path.join(root, "bar/test.js"), | |
6156 | path.join(root, "foo/test.js"), | |
6157 | path.join(root, "test.js") | |
6158 | ]); | |
6159 | }); | |
6160 | }); | |
6161 | ||
6162 | describe("if { files: 'foo/**/*.txt' } is present in a shareable config,", () => { | |
6f036462 TL |
6163 | |
6164 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6165 | cwd: root, | |
6166 | files: { | |
6167 | "node_modules/eslint-config-foo/index.js": `module.exports = ${JSON.stringify({ | |
6168 | overrides: [ | |
6169 | { | |
6170 | files: "foo/**/*.txt" | |
6171 | } | |
6172 | ] | |
6173 | })}`, | |
6174 | ".eslintrc.json": JSON.stringify({ | |
6175 | extends: "foo" | |
6176 | }), | |
6177 | "foo/nested/test.txt": "", | |
6178 | "foo/test.js": "", | |
6179 | "foo/test.txt": "", | |
6180 | "bar/test.js": "", | |
6181 | "bar/test.txt": "", | |
6182 | "test.js": "", | |
6183 | "test.txt": "" | |
6184 | } | |
56c4a2cb DC |
6185 | }); |
6186 | ||
6f036462 TL |
6187 | beforeEach(prepare); |
6188 | afterEach(cleanup); | |
6189 | ||
56c4a2cb | 6190 | it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { |
6f036462 | 6191 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6192 | const filePaths = (await engine.lintFiles(".")) |
6193 | .map(r => r.filePath) | |
6194 | .sort(); | |
6195 | ||
6196 | assert.deepStrictEqual(filePaths, [ | |
6197 | path.join(root, "bar/test.js"), | |
6198 | path.join(root, "foo/nested/test.txt"), | |
6199 | path.join(root, "foo/test.js"), | |
6200 | path.join(root, "foo/test.txt"), | |
6201 | path.join(root, "test.js") | |
6202 | ]); | |
6203 | }); | |
6204 | }); | |
6205 | ||
6206 | describe("if { files: 'foo/**/*.txt' } is present in a plugin config,", () => { | |
6f036462 TL |
6207 | |
6208 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6209 | cwd: root, | |
6210 | files: { | |
6211 | "node_modules/eslint-plugin-foo/index.js": `exports.configs = ${JSON.stringify({ | |
6212 | bar: { | |
6213 | overrides: [ | |
6214 | { | |
6215 | files: "foo/**/*.txt" | |
6216 | } | |
6217 | ] | |
6218 | } | |
6219 | })}`, | |
6220 | ".eslintrc.json": JSON.stringify({ | |
6221 | extends: "plugin:foo/bar" | |
6222 | }), | |
6223 | "foo/nested/test.txt": "", | |
6224 | "foo/test.js": "", | |
6225 | "foo/test.txt": "", | |
6226 | "bar/test.js": "", | |
6227 | "bar/test.txt": "", | |
6228 | "test.js": "", | |
6229 | "test.txt": "" | |
6230 | } | |
56c4a2cb DC |
6231 | }); |
6232 | ||
6f036462 TL |
6233 | beforeEach(prepare); |
6234 | afterEach(cleanup); | |
6235 | ||
56c4a2cb | 6236 | it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { |
6f036462 | 6237 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6238 | const filePaths = (await engine.lintFiles(".")) |
6239 | .map(r => r.filePath) | |
6240 | .sort(); | |
6241 | ||
6242 | assert.deepStrictEqual(filePaths, [ | |
6243 | path.join(root, "bar/test.js"), | |
6244 | path.join(root, "foo/nested/test.txt"), | |
6245 | path.join(root, "foo/test.js"), | |
6246 | path.join(root, "foo/test.txt"), | |
6247 | path.join(root, "test.js") | |
6248 | ]); | |
6249 | }); | |
6250 | }); | |
6251 | }); | |
6252 | ||
6253 | describe("'ignorePatterns', 'overrides[].files', and 'overrides[].excludedFiles' of the configuration that the '--config' option provided should be resolved from CWD.", () => { | |
6254 | const root = getFixturePath("cli-engine/config-and-overrides-files"); | |
6255 | ||
56c4a2cb | 6256 | describe("if { files: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { |
6f036462 TL |
6257 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
6258 | cwd: root, | |
6259 | files: { | |
6260 | "node_modules/myconf/.eslintrc.json": { | |
6261 | overrides: [ | |
6262 | { | |
6263 | files: "foo/*.js", | |
6264 | rules: { | |
6265 | eqeqeq: "error" | |
56c4a2cb | 6266 | } |
6f036462 TL |
6267 | } |
6268 | ] | |
6269 | }, | |
6270 | "node_modules/myconf/foo/test.js": "a == b", | |
6271 | "foo/test.js": "a == b" | |
6272 | } | |
56c4a2cb DC |
6273 | }); |
6274 | ||
6f036462 TL |
6275 | beforeEach(prepare); |
6276 | afterEach(cleanup); | |
6277 | ||
56c4a2cb | 6278 | it("'lintFiles()' with 'foo/test.js' should use the override entry.", async () => { |
6f036462 | 6279 | const engine = new ESLint({ |
56c4a2cb | 6280 | overrideConfigFile: "node_modules/myconf/.eslintrc.json", |
6f036462 | 6281 | cwd: getPath(), |
56c4a2cb DC |
6282 | ignore: false, |
6283 | useEslintrc: false | |
6284 | }); | |
6285 | const results = await engine.lintFiles("foo/test.js"); | |
6286 | ||
6287 | // Expected to be an 'eqeqeq' error because the file matches to `$CWD/foo/*.js`. | |
6288 | assert.deepStrictEqual(results, [ | |
6289 | { | |
6290 | errorCount: 1, | |
6f036462 | 6291 | filePath: path.join(getPath(), "foo/test.js"), |
56c4a2cb DC |
6292 | fixableErrorCount: 0, |
6293 | fixableWarningCount: 0, | |
6294 | messages: [ | |
6295 | { | |
6296 | column: 3, | |
6297 | endColumn: 5, | |
6298 | endLine: 1, | |
6299 | line: 1, | |
6300 | message: "Expected '===' and instead saw '=='.", | |
6301 | messageId: "unexpected", | |
6302 | nodeType: "BinaryExpression", | |
6303 | ruleId: "eqeqeq", | |
6304 | severity: 2 | |
6305 | } | |
6306 | ], | |
8f9d1d4d | 6307 | suppressedMessages: [], |
56c4a2cb DC |
6308 | source: "a == b", |
6309 | usedDeprecatedRules: [], | |
609c276f TL |
6310 | warningCount: 0, |
6311 | fatalErrorCount: 0 | |
56c4a2cb DC |
6312 | } |
6313 | ]); | |
6314 | }); | |
6315 | ||
6316 | it("'lintFiles()' with 'node_modules/myconf/foo/test.js' should NOT use the override entry.", async () => { | |
6f036462 | 6317 | const engine = new ESLint({ |
56c4a2cb DC |
6318 | overrideConfigFile: "node_modules/myconf/.eslintrc.json", |
6319 | cwd: root, | |
6320 | ignore: false, | |
6321 | useEslintrc: false | |
6322 | }); | |
6323 | const results = await engine.lintFiles("node_modules/myconf/foo/test.js"); | |
6324 | ||
6325 | // Expected to be no errors because the file doesn't match to `$CWD/foo/*.js`. | |
6326 | assert.deepStrictEqual(results, [ | |
6327 | { | |
6328 | errorCount: 0, | |
6f036462 | 6329 | filePath: path.join(getPath(), "node_modules/myconf/foo/test.js"), |
56c4a2cb DC |
6330 | fixableErrorCount: 0, |
6331 | fixableWarningCount: 0, | |
6332 | messages: [], | |
8f9d1d4d | 6333 | suppressedMessages: [], |
56c4a2cb | 6334 | usedDeprecatedRules: [], |
609c276f TL |
6335 | warningCount: 0, |
6336 | fatalErrorCount: 0 | |
56c4a2cb DC |
6337 | } |
6338 | ]); | |
6339 | }); | |
6340 | }); | |
6341 | ||
6342 | describe("if { files: '*', excludedFiles: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { | |
6f036462 TL |
6343 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
6344 | cwd: root, | |
6345 | files: { | |
6346 | "node_modules/myconf/.eslintrc.json": JSON.stringify({ | |
6347 | overrides: [ | |
6348 | { | |
6349 | files: "*", | |
6350 | excludedFiles: "foo/*.js", | |
6351 | rules: { | |
6352 | eqeqeq: "error" | |
56c4a2cb | 6353 | } |
6f036462 TL |
6354 | } |
6355 | ] | |
6356 | }), | |
6357 | "node_modules/myconf/foo/test.js": "a == b", | |
6358 | "foo/test.js": "a == b" | |
6359 | } | |
56c4a2cb DC |
6360 | }); |
6361 | ||
6f036462 TL |
6362 | beforeEach(prepare); |
6363 | afterEach(cleanup); | |
6364 | ||
56c4a2cb | 6365 | it("'lintFiles()' with 'foo/test.js' should NOT use the override entry.", async () => { |
6f036462 | 6366 | const engine = new ESLint({ |
56c4a2cb DC |
6367 | overrideConfigFile: "node_modules/myconf/.eslintrc.json", |
6368 | cwd: root, | |
6369 | ignore: false, | |
6370 | useEslintrc: false | |
6371 | }); | |
6372 | const results = await engine.lintFiles("foo/test.js"); | |
6373 | ||
6374 | // Expected to be no errors because the file matches to `$CWD/foo/*.js`. | |
6375 | assert.deepStrictEqual(results, [ | |
6376 | { | |
6377 | errorCount: 0, | |
6f036462 | 6378 | filePath: path.join(getPath(), "foo/test.js"), |
56c4a2cb DC |
6379 | fixableErrorCount: 0, |
6380 | fixableWarningCount: 0, | |
6381 | messages: [], | |
8f9d1d4d | 6382 | suppressedMessages: [], |
56c4a2cb | 6383 | usedDeprecatedRules: [], |
609c276f TL |
6384 | warningCount: 0, |
6385 | fatalErrorCount: 0 | |
56c4a2cb DC |
6386 | } |
6387 | ]); | |
6388 | }); | |
6389 | ||
6390 | it("'lintFiles()' with 'node_modules/myconf/foo/test.js' should use the override entry.", async () => { | |
6f036462 | 6391 | const engine = new ESLint({ |
56c4a2cb DC |
6392 | overrideConfigFile: "node_modules/myconf/.eslintrc.json", |
6393 | cwd: root, | |
6394 | ignore: false, | |
6395 | useEslintrc: false | |
6396 | }); | |
6397 | const results = await engine.lintFiles("node_modules/myconf/foo/test.js"); | |
6398 | ||
6399 | // Expected to be an 'eqeqeq' error because the file doesn't match to `$CWD/foo/*.js`. | |
6400 | assert.deepStrictEqual(results, [ | |
6401 | { | |
6402 | errorCount: 1, | |
6f036462 | 6403 | filePath: path.join(getPath(), "node_modules/myconf/foo/test.js"), |
56c4a2cb DC |
6404 | fixableErrorCount: 0, |
6405 | fixableWarningCount: 0, | |
6406 | messages: [ | |
6407 | { | |
6408 | column: 3, | |
6409 | endColumn: 5, | |
6410 | endLine: 1, | |
6411 | line: 1, | |
6412 | message: "Expected '===' and instead saw '=='.", | |
6413 | messageId: "unexpected", | |
6414 | nodeType: "BinaryExpression", | |
6415 | ruleId: "eqeqeq", | |
6416 | severity: 2 | |
6417 | } | |
6418 | ], | |
8f9d1d4d | 6419 | suppressedMessages: [], |
56c4a2cb DC |
6420 | source: "a == b", |
6421 | usedDeprecatedRules: [], | |
609c276f TL |
6422 | warningCount: 0, |
6423 | fatalErrorCount: 0 | |
56c4a2cb DC |
6424 | } |
6425 | ]); | |
6426 | }); | |
6427 | }); | |
6428 | ||
6429 | describe("if { ignorePatterns: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { | |
6f036462 TL |
6430 | const { prepare, cleanup, getPath } = createCustomTeardown({ |
6431 | cwd: root, | |
6432 | files: { | |
6433 | "node_modules/myconf/.eslintrc.json": JSON.stringify({ | |
6434 | ignorePatterns: ["!/node_modules/myconf", "foo/*.js"], | |
6435 | rules: { | |
6436 | eqeqeq: "error" | |
6437 | } | |
6438 | }), | |
6439 | "node_modules/myconf/foo/test.js": "a == b", | |
6440 | "foo/test.js": "a == b" | |
6441 | } | |
56c4a2cb DC |
6442 | }); |
6443 | ||
6f036462 TL |
6444 | beforeEach(prepare); |
6445 | afterEach(cleanup); | |
6446 | ||
56c4a2cb | 6447 | it("'lintFiles()' with '**/*.js' should iterate 'node_modules/myconf/foo/test.js' but not 'foo/test.js'.", async () => { |
6f036462 | 6448 | const engine = new ESLint({ |
56c4a2cb | 6449 | overrideConfigFile: "node_modules/myconf/.eslintrc.json", |
6f036462 | 6450 | cwd: getPath(), |
56c4a2cb DC |
6451 | useEslintrc: false |
6452 | }); | |
6453 | const files = (await engine.lintFiles("**/*.js")) | |
6454 | .map(r => r.filePath) | |
6455 | .sort(); | |
6456 | ||
6457 | assert.deepStrictEqual(files, [ | |
6458 | path.join(root, "node_modules/myconf/foo/test.js") | |
6459 | ]); | |
6460 | }); | |
6461 | }); | |
6462 | }); | |
6463 | ||
6464 | describe("plugin conflicts", () => { | |
6465 | let uid = 0; | |
6f036462 | 6466 | const root = getFixturePath("cli-engine/plugin-conflicts-"); |
56c4a2cb DC |
6467 | |
6468 | /** | |
6469 | * Verify thrown errors. | |
6470 | * @param {() => Promise<any>} f The function to run and throw. | |
6471 | * @param {Record<string, any>} props The properties to verify. | |
6472 | * @returns {Promise<void>} void | |
6473 | */ | |
6474 | async function assertThrows(f, props) { | |
6475 | try { | |
6476 | await f(); | |
6477 | } catch (error) { | |
6478 | for (const [key, value] of Object.entries(props)) { | |
6479 | assert.deepStrictEqual(error[key], value, key); | |
6480 | } | |
6481 | return; | |
6482 | } | |
6483 | ||
6484 | assert.fail("Function should throw an error, but not."); | |
6485 | } | |
6486 | ||
6487 | describe("between a config file and linear extendees.", () => { | |
6f036462 TL |
6488 | |
6489 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6490 | cwd: `${root}${++uid}`, | |
6491 | files: { | |
6492 | "node_modules/eslint-plugin-foo/index.js": "", | |
6493 | "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", | |
6494 | "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ | |
6495 | extends: ["two"], | |
6496 | plugins: ["foo"] | |
6497 | })}`, | |
6498 | "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", | |
6499 | "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ | |
6500 | plugins: ["foo"] | |
6501 | })}`, | |
6502 | ".eslintrc.json": JSON.stringify({ | |
6503 | extends: ["one"], | |
6504 | plugins: ["foo"] | |
6505 | }), | |
6506 | "test.js": "" | |
6507 | } | |
56c4a2cb DC |
6508 | }); |
6509 | ||
6f036462 TL |
6510 | beforeEach(prepare); |
6511 | afterEach(cleanup); | |
6512 | ||
56c4a2cb | 6513 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file.)", async () => { |
6f036462 | 6514 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6515 | |
6516 | await engine.lintFiles("test.js"); | |
6517 | }); | |
6518 | }); | |
6519 | ||
6520 | describe("between a config file and same-depth extendees.", () => { | |
6f036462 TL |
6521 | |
6522 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6523 | cwd: `${root}${++uid}`, | |
6524 | files: { | |
6525 | "node_modules/eslint-plugin-foo/index.js": "", | |
6526 | "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", | |
6527 | "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ | |
6528 | plugins: ["foo"] | |
6529 | })}`, | |
6530 | "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", | |
6531 | "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ | |
6532 | plugins: ["foo"] | |
6533 | })}`, | |
6534 | ".eslintrc.json": JSON.stringify({ | |
6535 | extends: ["one", "two"], | |
6536 | plugins: ["foo"] | |
6537 | }), | |
6538 | "test.js": "" | |
6539 | } | |
56c4a2cb DC |
6540 | }); |
6541 | ||
6f036462 TL |
6542 | beforeEach(prepare); |
6543 | afterEach(cleanup); | |
6544 | ||
56c4a2cb | 6545 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file.)", async () => { |
6f036462 | 6546 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6547 | |
6548 | await engine.lintFiles("test.js"); | |
6549 | }); | |
6550 | }); | |
6551 | ||
6552 | describe("between two config files in different directories, with single node_modules.", () => { | |
6f036462 TL |
6553 | |
6554 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6555 | cwd: `${root}${++uid}`, | |
6556 | files: { | |
6557 | "node_modules/eslint-plugin-foo/index.js": "", | |
6558 | ".eslintrc.json": JSON.stringify({ | |
6559 | plugins: ["foo"] | |
6560 | }), | |
6561 | "subdir/.eslintrc.json": JSON.stringify({ | |
6562 | plugins: ["foo"] | |
6563 | }), | |
6564 | "subdir/test.js": "" | |
6565 | } | |
56c4a2cb DC |
6566 | }); |
6567 | ||
6f036462 TL |
6568 | beforeEach(prepare); |
6569 | afterEach(cleanup); | |
6570 | ||
56c4a2cb | 6571 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files, but node_modules directory is unique.)", async () => { |
6f036462 | 6572 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6573 | |
6574 | await engine.lintFiles("subdir/test.js"); | |
6575 | }); | |
6576 | }); | |
6577 | ||
6578 | describe("between two config files in different directories, with multiple node_modules.", () => { | |
6f036462 TL |
6579 | |
6580 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6581 | cwd: `${root}${++uid}`, | |
6582 | files: { | |
6583 | "node_modules/eslint-plugin-foo/index.js": "", | |
6584 | ".eslintrc.json": JSON.stringify({ | |
6585 | plugins: ["foo"] | |
6586 | }), | |
6587 | "subdir/node_modules/eslint-plugin-foo/index.js": "", | |
6588 | "subdir/.eslintrc.json": JSON.stringify({ | |
6589 | plugins: ["foo"] | |
6590 | }), | |
6591 | "subdir/test.js": "" | |
6592 | } | |
56c4a2cb DC |
6593 | }); |
6594 | ||
6f036462 TL |
6595 | beforeEach(prepare); |
6596 | afterEach(cleanup); | |
6597 | ||
56c4a2cb | 6598 | it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files.)", async () => { |
6f036462 | 6599 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6600 | |
6601 | await assertThrows( | |
6602 | () => engine.lintFiles("subdir/test.js"), | |
6603 | { | |
6604 | message: `Plugin "foo" was conflicted between "subdir${path.sep}.eslintrc.json" and ".eslintrc.json".`, | |
6605 | messageTemplate: "plugin-conflict", | |
6606 | messageData: { | |
6607 | pluginId: "foo", | |
6608 | plugins: [ | |
6609 | { | |
6f036462 | 6610 | filePath: path.join(getPath(), "subdir/node_modules/eslint-plugin-foo/index.js"), |
56c4a2cb DC |
6611 | importerName: `subdir${path.sep}.eslintrc.json` |
6612 | }, | |
6613 | { | |
6f036462 | 6614 | filePath: path.join(getPath(), "node_modules/eslint-plugin-foo/index.js"), |
56c4a2cb DC |
6615 | importerName: ".eslintrc.json" |
6616 | } | |
6617 | ] | |
6618 | } | |
6619 | } | |
6620 | ); | |
6621 | }); | |
6622 | }); | |
6623 | ||
6624 | describe("between '--config' option and a regular config file, with single node_modules.", () => { | |
6f036462 TL |
6625 | |
6626 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6627 | cwd: `${root}${++uid}`, | |
6628 | files: { | |
6629 | "node_modules/eslint-plugin-foo/index.js": "", | |
6630 | "node_modules/mine/.eslintrc.json": JSON.stringify({ | |
6631 | plugins: ["foo"] | |
6632 | }), | |
6633 | ".eslintrc.json": JSON.stringify({ | |
6634 | plugins: ["foo"] | |
6635 | }), | |
6636 | "test.js": "" | |
6637 | } | |
56c4a2cb DC |
6638 | }); |
6639 | ||
6f036462 TL |
6640 | beforeEach(prepare); |
6641 | afterEach(cleanup); | |
6642 | ||
56c4a2cb | 6643 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files, but node_modules directory is unique.)", async () => { |
6f036462 TL |
6644 | const engine = new ESLint({ |
6645 | cwd: getPath(), | |
56c4a2cb DC |
6646 | overrideConfigFile: "node_modules/mine/.eslintrc.json" |
6647 | }); | |
6648 | ||
6649 | await engine.lintFiles("test.js"); | |
6650 | }); | |
6651 | }); | |
6652 | ||
6653 | describe("between '--config' option and a regular config file, with multiple node_modules.", () => { | |
6f036462 TL |
6654 | |
6655 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6656 | cwd: `${root}${++uid}`, | |
6657 | files: { | |
6658 | "node_modules/eslint-plugin-foo/index.js": "", | |
6659 | "node_modules/mine/node_modules/eslint-plugin-foo/index.js": "", | |
6660 | "node_modules/mine/.eslintrc.json": JSON.stringify({ | |
6661 | plugins: ["foo"] | |
6662 | }), | |
6663 | ".eslintrc.json": JSON.stringify({ | |
6664 | plugins: ["foo"] | |
6665 | }), | |
6666 | "test.js": "" | |
6667 | } | |
56c4a2cb DC |
6668 | }); |
6669 | ||
6f036462 TL |
6670 | beforeEach(prepare); |
6671 | afterEach(cleanup); | |
6672 | ||
56c4a2cb | 6673 | it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files.)", async () => { |
6f036462 TL |
6674 | const engine = new ESLint({ |
6675 | cwd: getPath(), | |
56c4a2cb DC |
6676 | overrideConfigFile: "node_modules/mine/.eslintrc.json" |
6677 | }); | |
6678 | ||
6679 | await assertThrows( | |
6680 | () => engine.lintFiles("test.js"), | |
6681 | { | |
6682 | message: "Plugin \"foo\" was conflicted between \"--config\" and \".eslintrc.json\".", | |
6683 | messageTemplate: "plugin-conflict", | |
6684 | messageData: { | |
6685 | pluginId: "foo", | |
6686 | plugins: [ | |
6687 | { | |
6f036462 | 6688 | filePath: path.join(getPath(), "node_modules/mine/node_modules/eslint-plugin-foo/index.js"), |
56c4a2cb DC |
6689 | importerName: "--config" |
6690 | }, | |
6691 | { | |
6f036462 | 6692 | filePath: path.join(getPath(), "node_modules/eslint-plugin-foo/index.js"), |
56c4a2cb DC |
6693 | importerName: ".eslintrc.json" |
6694 | } | |
6695 | ] | |
6696 | } | |
6697 | } | |
6698 | ); | |
6699 | }); | |
6700 | }); | |
6701 | ||
6702 | describe("between '--plugin' option and a regular config file, with single node_modules.", () => { | |
6f036462 TL |
6703 | |
6704 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6705 | cwd: `${root}${++uid}`, | |
6706 | files: { | |
6707 | "node_modules/eslint-plugin-foo/index.js": "", | |
6708 | "subdir/.eslintrc.json": JSON.stringify({ | |
6709 | plugins: ["foo"] | |
6710 | }), | |
6711 | "subdir/test.js": "" | |
6712 | } | |
56c4a2cb DC |
6713 | }); |
6714 | ||
6f036462 TL |
6715 | |
6716 | beforeEach(prepare); | |
6717 | afterEach(cleanup); | |
6718 | ||
56c4a2cb | 6719 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from both CWD and the base directory of the entry config file, but node_modules directory is unique.)", async () => { |
6f036462 TL |
6720 | const engine = new ESLint({ |
6721 | cwd: getPath(), | |
56c4a2cb DC |
6722 | overrideConfig: { plugins: ["foo"] } |
6723 | }); | |
6724 | ||
6725 | await engine.lintFiles("subdir/test.js"); | |
6726 | }); | |
6727 | }); | |
6728 | ||
6729 | describe("between '--plugin' option and a regular config file, with multiple node_modules.", () => { | |
6f036462 TL |
6730 | |
6731 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6732 | cwd: `${root}${++uid}`, | |
6733 | files: { | |
6734 | "node_modules/eslint-plugin-foo/index.js": "", | |
6735 | "subdir/node_modules/eslint-plugin-foo/index.js": "", | |
6736 | "subdir/.eslintrc.json": JSON.stringify({ | |
6737 | plugins: ["foo"] | |
6738 | }), | |
6739 | "subdir/test.js": "" | |
6740 | } | |
56c4a2cb DC |
6741 | }); |
6742 | ||
6f036462 TL |
6743 | beforeEach(prepare); |
6744 | afterEach(cleanup); | |
6745 | ||
56c4a2cb | 6746 | it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from both CWD and the base directory of the entry config file.)", async () => { |
6f036462 TL |
6747 | const engine = new ESLint({ |
6748 | cwd: getPath(), | |
56c4a2cb DC |
6749 | overrideConfig: { plugins: ["foo"] } |
6750 | }); | |
6751 | ||
6752 | await assertThrows( | |
6753 | () => engine.lintFiles("subdir/test.js"), | |
6754 | { | |
6755 | message: `Plugin "foo" was conflicted between "CLIOptions" and "subdir${path.sep}.eslintrc.json".`, | |
6756 | messageTemplate: "plugin-conflict", | |
6757 | messageData: { | |
6758 | pluginId: "foo", | |
6759 | plugins: [ | |
6760 | { | |
6f036462 | 6761 | filePath: path.join(getPath(), "node_modules/eslint-plugin-foo/index.js"), |
56c4a2cb DC |
6762 | importerName: "CLIOptions" |
6763 | }, | |
6764 | { | |
6f036462 | 6765 | filePath: path.join(getPath(), "subdir/node_modules/eslint-plugin-foo/index.js"), |
56c4a2cb DC |
6766 | importerName: `subdir${path.sep}.eslintrc.json` |
6767 | } | |
6768 | ] | |
6769 | } | |
6770 | } | |
6771 | ); | |
6772 | }); | |
6773 | }); | |
6774 | ||
6775 | describe("'--resolve-plugins-relative-to' option overrides the location that ESLint load plugins from.", () => { | |
6f036462 TL |
6776 | |
6777 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6778 | cwd: `${root}${++uid}`, | |
6779 | files: { | |
6780 | "node_modules/eslint-plugin-foo/index.js": "", | |
6781 | ".eslintrc.json": JSON.stringify({ | |
6782 | plugins: ["foo"] | |
6783 | }), | |
6784 | "subdir/node_modules/eslint-plugin-foo/index.js": "", | |
6785 | "subdir/.eslintrc.json": JSON.stringify({ | |
6786 | plugins: ["foo"] | |
6787 | }), | |
6788 | "subdir/test.js": "" | |
6789 | } | |
56c4a2cb DC |
6790 | }); |
6791 | ||
6f036462 TL |
6792 | beforeEach(prepare); |
6793 | afterEach(cleanup); | |
6794 | ||
56c4a2cb | 6795 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from '--resolve-plugins-relative-to'.)", async () => { |
6f036462 TL |
6796 | const engine = new ESLint({ |
6797 | cwd: getPath(), | |
6798 | resolvePluginsRelativeTo: getPath() | |
56c4a2cb DC |
6799 | }); |
6800 | ||
6801 | await engine.lintFiles("subdir/test.js"); | |
6802 | }); | |
6803 | }); | |
6804 | ||
6805 | describe("between two config files with different target files.", () => { | |
6f036462 TL |
6806 | |
6807 | const { prepare, cleanup, getPath } = createCustomTeardown({ | |
6808 | cwd: `${root}${++uid}`, | |
6809 | files: { | |
6810 | "one/node_modules/eslint-plugin-foo/index.js": "", | |
6811 | "one/.eslintrc.json": JSON.stringify({ | |
6812 | plugins: ["foo"] | |
6813 | }), | |
6814 | "one/test.js": "", | |
6815 | "two/node_modules/eslint-plugin-foo/index.js": "", | |
6816 | "two/.eslintrc.json": JSON.stringify({ | |
6817 | plugins: ["foo"] | |
6818 | }), | |
6819 | "two/test.js": "" | |
6820 | } | |
56c4a2cb DC |
6821 | }); |
6822 | ||
6f036462 TL |
6823 | beforeEach(prepare); |
6824 | afterEach(cleanup); | |
6825 | ||
56c4a2cb | 6826 | it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file for each target file. Not related to each other.)", async () => { |
6f036462 | 6827 | const engine = new ESLint({ cwd: getPath() }); |
56c4a2cb DC |
6828 | const results = await engine.lintFiles("*/test.js"); |
6829 | ||
6830 | assert.strictEqual(results.length, 2); | |
6831 | }); | |
6832 | }); | |
6833 | }); | |
8f9d1d4d DC |
6834 | |
6835 | describe("loading rules", () => { | |
6836 | it("should not load unused core rules", done => { | |
6837 | let calledDone = false; | |
6838 | ||
6839 | const cwd = getFixturePath("lazy-loading-rules"); | |
6840 | const pattern = "foo.js"; | |
6841 | const usedRules = ["semi"]; | |
6842 | ||
6843 | const forkedProcess = childProcess.fork( | |
6844 | path.join(__dirname, "../../_utils/test-lazy-loading-rules.js"), | |
6845 | [cwd, pattern, String(usedRules)] | |
6846 | ); | |
6847 | ||
6848 | // this is an error message | |
6849 | forkedProcess.on("message", ({ message, stack }) => { | |
6850 | if (calledDone) { | |
6851 | return; | |
6852 | } | |
6853 | calledDone = true; | |
6854 | ||
6855 | const error = new Error(message); | |
6856 | ||
6857 | error.stack = stack; | |
6858 | done(error); | |
6859 | }); | |
6860 | ||
6861 | forkedProcess.on("exit", exitCode => { | |
6862 | if (calledDone) { | |
6863 | return; | |
6864 | } | |
6865 | calledDone = true; | |
6866 | ||
6867 | if (exitCode === 0) { | |
6868 | done(); | |
6869 | } else { | |
6870 | done(new Error("Forked process exited with a non-zero exit code")); | |
6871 | } | |
6872 | }); | |
6873 | }); | |
6874 | }); | |
56c4a2cb | 6875 | }); |