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