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