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