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