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