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