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