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