diff --git a/.eslintrc.js b/.eslintrc.js index d4e2587afa6..1a39568fee0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,20 @@ +/* + * IMPORTANT! + * + * Any changes made to this file must also be made to eslint.config.js. + * + * Internally, ESLint is using the eslint.config.js file to lint itself. + * This file is needed too, because: + * + * 1. There are tests that expect .eslintrc.js to be present to actually run. + * 2. ESLint VS Code extension expects eslintrc config files to be + * present to work correctly. + * + * Once we no longer need to support both eslintrc and flat config, we will + * remove this file. + */ + + "use strict"; const path = require("path"); diff --git a/bin/eslint.js b/bin/eslint.js index 0f76fc92e1f..7094ac77bc4 100755 --- a/bin/eslint.js +++ b/bin/eslint.js @@ -9,9 +9,6 @@ "use strict"; -// to use V8's code cache to speed up instantiation time -require("v8-compile-cache"); - // must do this initialization *before* other requires in order to work if (process.argv.includes("--debug")) { require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*"); @@ -137,6 +134,7 @@ ${message}`); // Otherwise, call the CLI. process.exitCode = await require("../lib/cli").execute( process.argv, - process.argv.includes("--stdin") ? await readStdin() : null + process.argv.includes("--stdin") ? await readStdin() : null, + true ); }()).catch(onFatalError); diff --git a/docs/src/user-guide/configuring/configuration-files-new.md b/docs/src/user-guide/configuring/configuration-files-new.md index 56af3a55e6a..87844f5ecd5 100644 --- a/docs/src/user-guide/configuring/configuration-files-new.md +++ b/docs/src/user-guide/configuring/configuration-files-new.md @@ -10,7 +10,7 @@ eleventyNavigation: --- ::: warning -This is an experimental feature that is not enabled by default. You can use the configuration system described on this page by using the `FlatESLint` class, the `FlatRuleTester` class, or by setting `configType: "flat"` in the `Linter` class. +This is an experimental feature. To opt-in, place a `eslint.config.js` file in the root of your project. If you are using the API, you can use the configuration system described on this page by using the `FlatESLint` class, the `FlatRuleTester` class, or by setting `configType: "flat"` in the `Linter` class. ::: ## Configuration File diff --git a/eslint.config.js b/eslint.config.js index 50f375d3316..8df362209a1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -5,12 +5,29 @@ "use strict"; +/* + * IMPORTANT! + * + * Any changes made to this file must also be made to .eslintrc.js. + * + * Internally, ESLint is using the eslint.config.js file to lint itself. + * The .eslintrc.js file is needed too, because: + * + * 1. There are tests that expect .eslintrc.js to be present to actually run. + * 2. ESLint VS Code extension expects eslintrc config files to be + * present to work correctly. + * + * Once we no longer need to support both eslintrc and flat config, we will + * remove .eslintrc.js. + */ + //----------------------------------------------------------------------------- // Requirements //----------------------------------------------------------------------------- const path = require("path"); const internalPlugin = require("eslint-plugin-internal-rules"); +const eslintPlugin = require("eslint-plugin-eslint-plugin"); const { FlatCompat } = require("@eslint/eslintrc"); const globals = require("globals"); @@ -24,7 +41,6 @@ const compat = new FlatCompat({ const INTERNAL_FILES = { CLI_ENGINE_PATTERN: "lib/cli-engine/**/*", - INIT_PATTERN: "lib/init/**/*", LINTER_PATTERN: "lib/linter/**/*", RULE_TESTER_PATTERN: "lib/rule-tester/**/*", RULES_PATTERN: "lib/rules/**/*", @@ -60,11 +76,6 @@ function createInternalFilesPatterns(pattern = null) { })); } - -//----------------------------------------------------------------------------- -// Config -//----------------------------------------------------------------------------- - module.exports = [ ...compat.extends("eslint", "plugin:eslint-plugin/recommended"), { @@ -101,13 +112,25 @@ module.exports = [ "eslint-plugin/test-case-shorthand-strings": "error", "internal-rules/multiline-comment-style": "error" } - + }, + { + files: ["tools/*.js"], + rules: { + "no-console": "off" + } }, { files: ["lib/rules/*", "tools/internal-rules/*"], - ignores: ["index.js"], + ignores: ["**/index.js"], rules: { - "eslint-plugin/prefer-object-rule": "error", + ...eslintPlugin.configs["rules-recommended"].rules, + "eslint-plugin/no-missing-message-ids": "error", + "eslint-plugin/no-unused-message-ids": "error", + "eslint-plugin/prefer-message-ids": "error", + "eslint-plugin/prefer-placeholders": "error", + "eslint-plugin/prefer-replace-text": "error", + "eslint-plugin/report-message-format": ["error", "[^a-z].*\\.$"], + "eslint-plugin/require-meta-docs-description": ["error", { pattern: "^(Enforce|Require|Disallow)" }], "internal-rules/no-invalid-meta": "error" } }, @@ -119,7 +142,16 @@ module.exports = [ } }, { - files: ["tests/**/*"], + files: ["tests/lib/rules/*", "tests/tools/internal-rules/*"], + rules: { + ...eslintPlugin.configs["tests-recommended"].rules, + "eslint-plugin/prefer-output-null": "error", + "eslint-plugin/test-case-property-ordering": "error", + "eslint-plugin/test-case-shorthand-strings": "error" + } + }, + { + files: ["tests/**/*.js"], languageOptions: { globals: { ...globals.mocha @@ -147,17 +179,7 @@ module.exports = [ files: [INTERNAL_FILES.CLI_ENGINE_PATTERN], rules: { "n/no-restricted-require": ["error", [ - ...createInternalFilesPatterns(INTERNAL_FILES.CLI_ENGINE_PATTERN), - resolveAbsolutePath("lib/init/index.js") - ]] - } - }, - { - files: [INTERNAL_FILES.INIT_PATTERN], - rules: { - "n/no-restricted-require": ["error", [ - ...createInternalFilesPatterns(INTERNAL_FILES.INIT_PATTERN), - resolveAbsolutePath("lib/rule-tester/index.js") + ...createInternalFilesPatterns(INTERNAL_FILES.CLI_ENGINE_PATTERN) ]] } }, @@ -168,7 +190,6 @@ module.exports = [ ...createInternalFilesPatterns(INTERNAL_FILES.LINTER_PATTERN), "fs", resolveAbsolutePath("lib/cli-engine/index.js"), - resolveAbsolutePath("lib/init/index.js"), resolveAbsolutePath("lib/rule-tester/index.js") ]] } @@ -180,7 +201,6 @@ module.exports = [ ...createInternalFilesPatterns(INTERNAL_FILES.RULES_PATTERN), "fs", resolveAbsolutePath("lib/cli-engine/index.js"), - resolveAbsolutePath("lib/init/index.js"), resolveAbsolutePath("lib/linter/index.js"), resolveAbsolutePath("lib/rule-tester/index.js"), resolveAbsolutePath("lib/source-code/index.js") @@ -193,7 +213,6 @@ module.exports = [ "n/no-restricted-require": ["error", [ ...createInternalFilesPatterns(), resolveAbsolutePath("lib/cli-engine/index.js"), - resolveAbsolutePath("lib/init/index.js"), resolveAbsolutePath("lib/linter/index.js"), resolveAbsolutePath("lib/rule-tester/index.js"), resolveAbsolutePath("lib/source-code/index.js") @@ -207,7 +226,6 @@ module.exports = [ ...createInternalFilesPatterns(INTERNAL_FILES.SOURCE_CODE_PATTERN), "fs", resolveAbsolutePath("lib/cli-engine/index.js"), - resolveAbsolutePath("lib/init/index.js"), resolveAbsolutePath("lib/linter/index.js"), resolveAbsolutePath("lib/rule-tester/index.js"), resolveAbsolutePath("lib/rules/index.js") @@ -219,8 +237,7 @@ module.exports = [ rules: { "n/no-restricted-require": ["error", [ ...createInternalFilesPatterns(INTERNAL_FILES.RULE_TESTER_PATTERN), - resolveAbsolutePath("lib/cli-engine/index.js"), - resolveAbsolutePath("lib/init/index.js") + resolveAbsolutePath("lib/cli-engine/index.js") ]] } } diff --git a/lib/cli.js b/lib/cli.js index f09d143d255..2fca65c1908 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -6,7 +6,7 @@ "use strict"; /* - * The CLI object should *not* call process.exit() directly. It should only return + * NOTE: The CLI object should *not* call process.exit() directly. It should only return * exit codes. This allows other programs to use the CLI object and still control * when the program exits. */ @@ -19,9 +19,14 @@ const fs = require("fs"), path = require("path"), { promisify } = require("util"), { ESLint } = require("./eslint"), - CLIOptions = require("./options"), + { FlatESLint } = require("./eslint/flat-eslint"), + createCLIOptions = require("./options"), log = require("./shared/logging"), RuntimeInfo = require("./shared/runtime-info"); +const { Legacy: { naming } } = require("@eslint/eslintrc"); +const { findFlatConfigFile } = require("./eslint/flat-eslint"); +const { gitignoreToMinimatch } = require("@humanwhocodes/gitignore-to-minimatch"); +const { ModuleImporter } = require("@humanwhocodes/module-importer"); const debug = require("debug")("eslint:cli"); @@ -54,17 +59,20 @@ function quietFixPredicate(message) { } /** - * Translates the CLI options into the options expected by the CLIEngine. + * Translates the CLI options into the options expected by the ESLint constructor. * @param {ParsedCLIOptions} cliOptions The CLI options to translate. - * @returns {ESLintOptions} The options object for the CLIEngine. + * @param {"flat"|"eslintrc"} [configType="eslintrc"] The format of the + * config to generate. + * @returns {Promise} The options object for the ESLint constructor. * @private */ -function translateOptions({ +async function translateOptions({ cache, cacheFile, cacheLocation, cacheStrategy, config, + configLookup, env, errorOnUnmatchedPattern, eslintrc, @@ -85,19 +93,66 @@ function translateOptions({ resolvePluginsRelativeTo, rule, rulesdir -}) { - return { - allowInlineConfig: inlineConfig, - cache, - cacheLocation: cacheLocation || cacheFile, - cacheStrategy, - errorOnUnmatchedPattern, - extensions: ext, - fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true), - fixTypes: fixType, - ignore, - ignorePath, - overrideConfig: { +}, configType) { + + let overrideConfig, overrideConfigFile; + const importer = new ModuleImporter(); + + if (configType === "flat") { + overrideConfigFile = (typeof config === "string") ? config : !configLookup; + if (overrideConfigFile === false) { + overrideConfigFile = void 0; + } + + let globals = {}; + + if (global) { + globals = global.reduce((obj, name) => { + if (name.endsWith(":true")) { + obj[name.slice(0, -5)] = "writable"; + } else { + obj[name] = "readonly"; + } + return obj; + }, globals); + } + + overrideConfig = [{ + languageOptions: { + globals, + parserOptions: parserOptions || {} + }, + rules: rule ? rule : {} + }]; + + if (parser) { + overrideConfig[0].languageOptions.parser = await importer.import(parser); + } + + if (plugin) { + const plugins = {}; + + for (const pluginName of plugin) { + + const shortName = naming.getShorthandName(pluginName, "eslint-plugin"); + const longName = naming.normalizePackageName(pluginName, "eslint-plugin"); + + plugins[shortName] = await importer.import(longName); + } + + overrideConfig[0].plugins = plugins; + } + + if (ignorePattern) { + overrideConfig.push({ + ignores: ignorePattern.map(gitignoreToMinimatch) + }); + } + + } else { + overrideConfigFile = config; + + overrideConfig = { env: env && env.reduce((obj, name) => { obj[name] = true; return obj; @@ -115,13 +170,32 @@ function translateOptions({ parserOptions, plugins: plugin, rules: rule - }, - overrideConfigFile: config, - reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0, - resolvePluginsRelativeTo, - rulePaths: rulesdir, - useEslintrc: eslintrc + }; + } + + const options = { + allowInlineConfig: inlineConfig, + cache, + cacheLocation: cacheLocation || cacheFile, + cacheStrategy, + errorOnUnmatchedPattern, + fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true), + fixTypes: fixType, + ignore, + ignorePath, + overrideConfig, + overrideConfigFile, + reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0 }; + + if (configType !== "flat") { + options.resolvePluginsRelativeTo = resolvePluginsRelativeTo; + options.rulePaths = rulesdir; + options.useEslintrc = eslintrc; + options.extensions = ext; + } + + return options; } /** @@ -218,19 +292,34 @@ const cli = { * Executes the CLI based on an array of arguments that is passed in. * @param {string|Array|Object} args The arguments to process. * @param {string} [text] The text to lint (used for TTY). + * @param {boolean} [allowFlatConfig] Whether or not to allow flat config. * @returns {Promise} The exit code for the operation. */ - async execute(args, text) { + async execute(args, text, allowFlatConfig) { if (Array.isArray(args)) { debug("CLI args: %o", args.slice(2)); } + /* + * Before doing anything, we need to see if we are using a + * flat config file. If so, then we need to change the way command + * line args are parsed. This is temporary, and when we fully + * switch to flat config we can remove this logic. + */ + + const usingFlatConfig = allowFlatConfig && !!(await findFlatConfigFile(process.cwd())); + + debug("Using flat config?", usingFlatConfig); + + const CLIOptions = createCLIOptions(usingFlatConfig); + /** @type {ParsedCLIOptions} */ let options; try { options = CLIOptions.parse(args); } catch (error) { + debug("Error parsing CLI options:", error.message); log.error(error.message); return 2; } @@ -251,6 +340,7 @@ const cli = { log.info(RuntimeInfo.environment()); return 0; } catch (err) { + debug("Error retrieving environment info"); log.error(err.message); return 2; } @@ -266,7 +356,9 @@ const cli = { return 2; } - const engine = new ESLint(translateOptions(options)); + const engine = usingFlatConfig + ? new FlatESLint(await translateOptions(options, "flat")) + : new ESLint(await translateOptions(options)); const fileConfig = await engine.calculateConfigForFile(options.printConfig); @@ -289,7 +381,9 @@ const cli = { return 2; } - const engine = new ESLint(translateOptions(options)); + const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint; + + const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc")); let results; if (useStdin) { @@ -303,14 +397,14 @@ const cli = { if (options.fix) { debug("Fix mode enabled - applying fixes"); - await ESLint.outputFixes(results); + await ActiveESLint.outputFixes(results); } let resultsToPrint = results; if (options.quiet) { debug("Quiet mode enabled - filtering out warnings"); - resultsToPrint = ESLint.getErrorResults(resultsToPrint); + resultsToPrint = ActiveESLint.getErrorResults(resultsToPrint); } if (await printResults(engine, resultsToPrint, options.format, options.outputFile)) { diff --git a/lib/eslint/eslint-helpers.js b/lib/eslint/eslint-helpers.js index 5818d8d1039..442686e56bf 100644 --- a/lib/eslint/eslint-helpers.js +++ b/lib/eslint/eslint-helpers.js @@ -104,6 +104,8 @@ function isGlobPattern(pattern) { * false to not interpret glob patterns. * @param {string} args.cwd The current working directory to find from. * @param {FlatConfigArray} args.configs The configs for the current run. + * @param {boolean} args.errorOnUnmatchedPattern Determines if an unmatched pattern + * should throw an error. * @returns {Promise>} The fully resolved file paths. * @throws {AllFilesIgnoredError} If there are no results due to an ignore pattern. * @throws {NoFilesFoundError} If no files matched the given patterns. @@ -112,7 +114,8 @@ async function findFiles({ patterns, globInputPaths, cwd, - configs + configs, + errorOnUnmatchedPattern }) { const results = []; @@ -222,14 +225,16 @@ async function findFiles({ } // no files were found - throw new NoFilesFoundError(globbyPattern, globInputPaths); + if (errorOnUnmatchedPattern) { + throw new NoFilesFoundError(globbyPattern, globInputPaths); + } } /* eslint-enable no-unreachable-loop -- Go back to normal. */ } // there were patterns that didn't match anything, tell the user - if (missingPatterns.length) { + if (errorOnUnmatchedPattern && missingPatterns.length) { throw new NoFilesFoundError(missingPatterns[0], globInputPaths); } @@ -322,6 +327,7 @@ function createIgnoreResult(filePath, baseDir) { message } ], + suppressedMessages: [], errorCount: 0, warningCount: 1, fatalErrorCount: 0, @@ -378,7 +384,6 @@ function processOptions({ cacheStrategy = "metadata", cwd = process.cwd(), errorOnUnmatchedPattern = true, - extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature. fix = false, fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property. globInputPaths = true, @@ -405,6 +410,9 @@ function processOptions({ if (unknownOptionKeys.includes("envs")) { errors.push("'envs' has been removed."); } + if (unknownOptionKeys.includes("extensions")) { + errors.push("'extensions' has been removed."); + } if (unknownOptionKeys.includes("resolvePluginsRelativeTo")) { errors.push("'resolvePluginsRelativeTo' has been removed."); } @@ -451,9 +459,6 @@ function processOptions({ if (typeof errorOnUnmatchedPattern !== "boolean") { errors.push("'errorOnUnmatchedPattern' must be a boolean."); } - if (!isArrayOfNonEmptyString(extensions) && extensions !== null) { - errors.push("'extensions' must be an array of non-empty strings or null."); - } if (typeof fix !== "boolean" && typeof fix !== "function") { errors.push("'fix' must be a boolean or a function."); } @@ -507,7 +512,6 @@ function processOptions({ overrideConfig, cwd, errorOnUnmatchedPattern, - extensions, fix, fixTypes, globInputPaths, diff --git a/lib/eslint/flat-eslint.js b/lib/eslint/flat-eslint.js index 6755356f17c..e436c464014 100644 --- a/lib/eslint/flat-eslint.js +++ b/lib/eslint/flat-eslint.js @@ -73,7 +73,6 @@ const LintResultCache = require("../cli-engine/lint-result-cache"); * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files. * @property {string} [cwd] The value to use for the current working directory. * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`. - * @property {string[]} [extensions] An array of file extensions to check. * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean. * @property {string[]} [fixTypes] Array of rule types to apply fixes for. * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. @@ -321,8 +320,7 @@ async function calculateConfigArray(eslint, { configFile, ignore: shouldIgnore, ignorePath, - ignorePatterns, - extensions + ignorePatterns }) { // check for cached instance @@ -365,13 +363,6 @@ async function calculateConfigArray(eslint, { // add in any configured defaults configs.push(...slots.defaultConfigs); - // if there are any extensions, create configs for them for easier matching - if (extensions && extensions.length) { - configs.push({ - files: extensions.map(ext => `**/*${ext}`) - }); - } - let allIgnorePatterns = []; let ignoreFilePath; @@ -515,6 +506,7 @@ function verifyText({ const result = { filePath: filePath === "" ? filePath : path.resolve(filePath), messages, + suppressedMessages: linter.getSuppressedMessages(), ...calculateStatsPerFile(messages) }; @@ -689,11 +681,13 @@ class FlatESLint { results.forEach(result => { const filteredMessages = result.messages.filter(isErrorMessage); + const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage); if (filteredMessages.length > 0) { filtered.push({ ...result, messages: filteredMessages, + suppressedMessages: filteredSuppressedMessages, errorCount: filteredMessages.length, warningCount: 0, fixableErrorCount: result.fixableErrorCount, @@ -746,11 +740,12 @@ class FlatESLint { * calculated config for the given file. */ const config = configs.getConfig(filePath); + const allMessages = result.messages.concat(result.suppressedMessages); - for (const { ruleId } of result.messages) { + for (const { ruleId } of allMessages) { const rule = getRuleFromConfig(ruleId, config); - // ensure the rule exists exists + // ensure the rule exists if (!rule) { throw new TypeError(`Could not find the rule "${ruleId}".`); } @@ -786,8 +781,8 @@ class FlatESLint { fix, fixTypes, reportUnusedDisableDirectives, - extensions, - globInputPaths + globInputPaths, + errorOnUnmatchedPattern } = eslintOptions; const startTime = Date.now(); const usedConfigs = []; @@ -812,9 +807,9 @@ class FlatESLint { const filePaths = await findFiles({ patterns: typeof patterns === "string" ? [patterns] : patterns, cwd, - extensions, globInputPaths, - configs + configs, + errorOnUnmatchedPattern }); debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`); @@ -1127,6 +1122,7 @@ class FlatESLint { results.sort(compareResultsByFilePath); return formatter(results, { + cwd, get rulesMeta() { if (!rulesMeta) { rulesMeta = eslint.getRulesMetaForResults(results); @@ -1175,5 +1171,6 @@ class FlatESLint { //------------------------------------------------------------------------------ module.exports = { - FlatESLint + FlatESLint, + findFlatConfigFile }; diff --git a/lib/options.js b/lib/options.js index 6d06e3ddce1..0d95f2a6354 100644 --- a/lib/options.js +++ b/lib/options.js @@ -63,261 +63,309 @@ const optionator = require("optionator"); //------------------------------------------------------------------------------ // exports "parse(args)", "generateHelp()", and "generateHelpForOption(optionName)" -module.exports = optionator({ - prepend: "eslint [options] file.js [file.js] [dir]", - defaults: { - concatRepeatedArrays: true, - mergeRepeatedObjects: true - }, - options: [ - { - heading: "Basic configuration" - }, - { + +/** + * Creates the CLI options for ESLint. + * @param {boolean} usingFlatConfig Indicates if flat config is being used. + * @returns {Object} The opinionator instance. + */ +module.exports = function(usingFlatConfig) { + + let lookupFlag; + + if (usingFlatConfig) { + lookupFlag = { + option: "config-lookup", + type: "Boolean", + default: "true", + description: "Disable look up for eslint.config.js" + }; + } else { + lookupFlag = { option: "eslintrc", type: "Boolean", default: "true", description: "Disable use of configuration from .eslintrc.*" - }, - { - option: "config", - alias: "c", - type: "path::String", - description: "Use this configuration, overriding .eslintrc.* config options if present" - }, - { + }; + } + + let envFlag; + + if (!usingFlatConfig) { + envFlag = { option: "env", type: "[String]", description: "Specify environments" - }, - { + }; + } + + let extFlag; + + if (!usingFlatConfig) { + extFlag = { option: "ext", type: "[String]", description: "Specify JavaScript file extensions" - }, - { - option: "global", - type: "[String]", - description: "Define global variables" - }, - { - option: "parser", - type: "String", - description: "Specify the parser to be used" - }, - { - option: "parser-options", - type: "Object", - description: "Specify parser options" - }, - { + }; + } + + let resolvePluginsFlag; + + if (!usingFlatConfig) { + resolvePluginsFlag = { option: "resolve-plugins-relative-to", type: "path::String", description: "A folder where plugins should be resolved from, CWD by default" - }, - { - heading: "Specifying rules and plugins" - }, - { - option: "plugin", - type: "[String]", - description: "Specify plugins" - }, - { - option: "rule", - type: "Object", - description: "Specify rules" - }, - { + }; + } + + let rulesDirFlag; + + if (!usingFlatConfig) { + rulesDirFlag = { option: "rulesdir", type: "[path::String]", description: "Load additional rules from this directory. Deprecated: Use rules from plugins" - }, - { - heading: "Fixing problems" - }, - { - option: "fix", - type: "Boolean", - default: false, - description: "Automatically fix problems" - }, - { - option: "fix-dry-run", - type: "Boolean", - default: false, - description: "Automatically fix problems without saving the changes to the file system" - }, - { - option: "fix-type", - type: "Array", - description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)" - }, - { - heading: "Ignoring files" - }, - { - option: "ignore-path", - type: "path::String", - description: "Specify path of ignore file" - }, - { - option: "ignore", - type: "Boolean", - default: "true", - description: "Disable use of ignore files and patterns" - }, - { - option: "ignore-pattern", - type: "[String]", - description: "Pattern of files to ignore (in addition to those in .eslintignore)", - concatRepeatedArrays: [true, { - oneValuePerFlag: true - }] - }, - { - heading: "Using stdin" - }, - { - option: "stdin", - type: "Boolean", - default: "false", - description: "Lint code provided on " - }, - { - option: "stdin-filename", - type: "String", - description: "Specify filename to process STDIN as" - }, - { - heading: "Handling warnings" - }, - { - option: "quiet", - type: "Boolean", - default: "false", - description: "Report errors only" - }, - { - option: "max-warnings", - type: "Int", - default: "-1", - description: "Number of warnings to trigger nonzero exit code" - }, - { - heading: "Output" - }, - { - option: "output-file", - alias: "o", - type: "path::String", - description: "Specify file to write report to" - }, - { - option: "format", - alias: "f", - type: "String", - default: "stylish", - description: "Use a specific output format" - }, - { - option: "color", - type: "Boolean", - alias: "no-color", - description: "Force enabling/disabling of color" - }, - { - heading: "Inline configuration comments" - }, - { - option: "inline-config", - type: "Boolean", - default: "true", - description: "Prevent comments from changing config or rules" - }, - { - option: "report-unused-disable-directives", - type: "Boolean", - default: void 0, - description: "Adds reported errors for unused eslint-disable directives" - }, - { - heading: "Caching" - }, - { - option: "cache", - type: "Boolean", - default: "false", - description: "Only check changed files" - }, - { - option: "cache-file", - type: "path::String", - default: ".eslintcache", - description: "Path to the cache file. Deprecated: use --cache-location" - }, - { - option: "cache-location", - type: "path::String", - description: "Path to the cache file or directory" - }, - { - option: "cache-strategy", - dependsOn: ["cache"], - type: "String", - default: "metadata", - enum: ["metadata", "content"], - description: "Strategy to use for detecting changed files in the cache" - }, - { - heading: "Miscellaneous" - }, - { - option: "init", - type: "Boolean", - default: "false", - description: "Run config initialization wizard" - }, - { - option: "env-info", - type: "Boolean", - default: "false", - description: "Output execution environment information" - }, - { - option: "error-on-unmatched-pattern", - type: "Boolean", - default: "true", - description: "Prevent errors when pattern is unmatched" - }, - { - option: "exit-on-fatal-error", - type: "Boolean", - default: "false", - description: "Exit with exit code 2 in case of fatal error" - }, - { - option: "debug", - type: "Boolean", - default: false, - description: "Output debugging information" - }, - { - option: "help", - alias: "h", - type: "Boolean", - description: "Show help" - }, - { - option: "version", - alias: "v", - type: "Boolean", - description: "Output the version number" - }, - { - option: "print-config", - type: "path::String", - description: "Print the configuration for the given file" - } - ] -}); + }; + } + + return optionator({ + prepend: "eslint [options] file.js [file.js] [dir]", + defaults: { + concatRepeatedArrays: true, + mergeRepeatedObjects: true + }, + options: [ + { + heading: "Basic configuration" + }, + lookupFlag, + { + option: "config", + alias: "c", + type: "path::String", + description: usingFlatConfig + ? "Use this configuration instead of eslint.config.js" + : "Use this configuration, overriding .eslintrc.* config options if present" + }, + envFlag, + extFlag, + { + option: "global", + type: "[String]", + description: "Define global variables" + }, + { + option: "parser", + type: "String", + description: "Specify the parser to be used" + }, + { + option: "parser-options", + type: "Object", + description: "Specify parser options" + }, + resolvePluginsFlag, + { + heading: "Specifying rules and plugins" + }, + { + option: "plugin", + type: "[String]", + description: "Specify plugins" + }, + { + option: "rule", + type: "Object", + description: "Specify rules" + }, + rulesDirFlag, + { + heading: "Fixing problems" + }, + { + option: "fix", + type: "Boolean", + default: false, + description: "Automatically fix problems" + }, + { + option: "fix-dry-run", + type: "Boolean", + default: false, + description: "Automatically fix problems without saving the changes to the file system" + }, + { + option: "fix-type", + type: "Array", + description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)" + }, + { + heading: "Ignoring files" + }, + { + option: "ignore-path", + type: "path::String", + description: "Specify path of ignore file" + }, + { + option: "ignore", + type: "Boolean", + default: "true", + description: "Disable use of ignore files and patterns" + }, + { + option: "ignore-pattern", + type: "[String]", + description: "Pattern of files to ignore (in addition to those in .eslintignore)", + concatRepeatedArrays: [true, { + oneValuePerFlag: true + }] + }, + { + heading: "Using stdin" + }, + { + option: "stdin", + type: "Boolean", + default: "false", + description: "Lint code provided on " + }, + { + option: "stdin-filename", + type: "String", + description: "Specify filename to process STDIN as" + }, + { + heading: "Handling warnings" + }, + { + option: "quiet", + type: "Boolean", + default: "false", + description: "Report errors only" + }, + { + option: "max-warnings", + type: "Int", + default: "-1", + description: "Number of warnings to trigger nonzero exit code" + }, + { + heading: "Output" + }, + { + option: "output-file", + alias: "o", + type: "path::String", + description: "Specify file to write report to" + }, + { + option: "format", + alias: "f", + type: "String", + default: "stylish", + description: "Use a specific output format" + }, + { + option: "color", + type: "Boolean", + alias: "no-color", + description: "Force enabling/disabling of color" + }, + { + heading: "Inline configuration comments" + }, + { + option: "inline-config", + type: "Boolean", + default: "true", + description: "Prevent comments from changing config or rules" + }, + { + option: "report-unused-disable-directives", + type: "Boolean", + default: void 0, + description: "Adds reported errors for unused eslint-disable directives" + }, + { + heading: "Caching" + }, + { + option: "cache", + type: "Boolean", + default: "false", + description: "Only check changed files" + }, + { + option: "cache-file", + type: "path::String", + default: ".eslintcache", + description: "Path to the cache file. Deprecated: use --cache-location" + }, + { + option: "cache-location", + type: "path::String", + description: "Path to the cache file or directory" + }, + { + option: "cache-strategy", + dependsOn: ["cache"], + type: "String", + default: "metadata", + enum: ["metadata", "content"], + description: "Strategy to use for detecting changed files in the cache" + }, + { + heading: "Miscellaneous" + }, + { + option: "init", + type: "Boolean", + default: "false", + description: "Run config initialization wizard" + }, + { + option: "env-info", + type: "Boolean", + default: "false", + description: "Output execution environment information" + }, + { + option: "error-on-unmatched-pattern", + type: "Boolean", + default: "true", + description: "Prevent errors when pattern is unmatched" + }, + { + option: "exit-on-fatal-error", + type: "Boolean", + default: "false", + description: "Exit with exit code 2 in case of fatal error" + }, + { + option: "debug", + type: "Boolean", + default: false, + description: "Output debugging information" + }, + { + option: "help", + alias: "h", + type: "Boolean", + description: "Show help" + }, + { + option: "version", + alias: "v", + type: "Boolean", + description: "Output the version number" + }, + { + option: "print-config", + type: "path::String", + description: "Print the configuration for the given file" + } + ].filter(value => !!value) + }); +}; diff --git a/lib/rule-tester/flat-rule-tester.js b/lib/rule-tester/flat-rule-tester.js index 19bd8e354f6..f915924c78b 100644 --- a/lib/rule-tester/flat-rule-tester.js +++ b/lib/rule-tester/flat-rule-tester.js @@ -4,7 +4,7 @@ */ "use strict"; -/* eslint-env mocha -- Mocha/Jest wrapper */ +/* globals describe, it -- Mocha globals */ //------------------------------------------------------------------------------ // Requirements diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js index fe0e468916b..2af272bd25b 100644 --- a/lib/rule-tester/rule-tester.js +++ b/lib/rule-tester/rule-tester.js @@ -4,7 +4,7 @@ */ "use strict"; -/* eslint-env mocha -- Mocha wrapper */ +/* globals describe, it -- Mocha globals */ /* * This is a wrapper around mocha to allow for DRY unittests for eslint diff --git a/lib/rules/no-lone-blocks.js b/lib/rules/no-lone-blocks.js index 486a76ffdc9..eb97f958c3c 100644 --- a/lib/rules/no-lone-blocks.js +++ b/lib/rules/no-lone-blocks.js @@ -91,7 +91,7 @@ module.exports = { }; // ES6: report blocks without block-level bindings, or that's only child of another block - if (context.parserOptions.ecmaVersion >= 6) { + if (context.languageOptions.ecmaVersion >= 2015) { ruleDef = { BlockStatement(node) { if (isLoneBlock(node)) { diff --git a/package.json b/package.json index d5e2c392d4f..1c119318d6d 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.10.4", "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -92,8 +93,7 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "devDependencies": { "@babel/core": "^7.4.3", diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js index 4a929a62c11..848dba14671 100644 --- a/tests/bin/eslint.js +++ b/tests/bin/eslint.js @@ -82,7 +82,7 @@ describe("bin/eslint.js", () => { describe("reading from stdin", () => { it("has exit code 0 if no linting errors are reported", () => { - const child = runESLint(["--stdin", "--no-eslintrc"]); + const child = runESLint(["--stdin", "--no-config-lookup"]); child.stdin.write("var foo = bar;\n"); child.stdin.end(); @@ -92,7 +92,7 @@ describe("bin/eslint.js", () => { it("has exit code 0 if no linting errors are reported", () => { const child = runESLint([ "--stdin", - "--no-eslintrc", + "--no-config-lookup", "--rule", "{'no-extra-semi': 2}", "--fix-dry-run", @@ -128,7 +128,7 @@ describe("bin/eslint.js", () => { }); it("has exit code 1 if a syntax error is thrown", () => { - const child = runESLint(["--stdin", "--no-eslintrc"]); + const child = runESLint(["--stdin", "--no-config-lookup"]); child.stdin.write("This is not valid JS syntax.\n"); child.stdin.end(); @@ -136,7 +136,7 @@ describe("bin/eslint.js", () => { }); it("has exit code 2 if a syntax error is thrown when exit-on-fatal-error is true", () => { - const child = runESLint(["--stdin", "--no-eslintrc", "--exit-on-fatal-error"]); + const child = runESLint(["--stdin", "--no-config-lookup", "--exit-on-fatal-error"]); child.stdin.write("This is not valid JS syntax.\n"); child.stdin.end(); @@ -144,7 +144,7 @@ describe("bin/eslint.js", () => { }); it("has exit code 1 if a linting error occurs", () => { - const child = runESLint(["--stdin", "--no-eslintrc", "--rule", "semi:2"]); + const child = runESLint(["--stdin", "--no-config-lookup", "--rule", "semi:2"]); child.stdin.write("var foo = bar // <-- no semicolon\n"); child.stdin.end(); @@ -182,7 +182,7 @@ describe("bin/eslint.js", () => { ); it("successfully reads from an asynchronous pipe", () => { - const child = runESLint(["--stdin", "--no-eslintrc"]); + const child = runESLint(["--stdin", "--no-config-lookup"]); child.stdin.write("var foo = bar;\n"); return new Promise(resolve => setTimeout(resolve, 300)).then(() => { @@ -194,7 +194,7 @@ describe("bin/eslint.js", () => { }); it("successfully handles more than 4k data via stdin", () => { - const child = runESLint(["--stdin", "--no-eslintrc"]); + const child = runESLint(["--stdin", "--no-config-lookup"]); const large = fs.createReadStream(path.join(__dirname, "../bench/large.js"), "utf8"); large.pipe(child.stdin); @@ -205,9 +205,9 @@ describe("bin/eslint.js", () => { describe("running on files", () => { it("has exit code 0 if no linting errors occur", () => assertExitCode(runESLint(["bin/eslint.js"]), 0)); - it("has exit code 0 if a linting warning is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--env", "es2021", "--no-eslintrc", "--rule", "semi: [1, never]"]), 0)); - it("has exit code 1 if a linting error is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--env", "es2021", "--no-eslintrc", "--rule", "semi: [2, never]"]), 1)); - it("has exit code 1 if a syntax error is thrown", () => assertExitCode(runESLint(["README.md"]), 1)); + it("has exit code 0 if a linting warning is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--no-config-lookup", "--rule", "semi: [1, never]"]), 0)); + it("has exit code 1 if a linting error is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--no-config-lookup", "--rule", "semi: [2, never]"]), 1)); + it("has exit code 1 if a syntax error is thrown", () => assertExitCode(runESLint(["tests/fixtures/exit-on-fatal-error/fatal-error.js", "--no-ignore"]), 1)); }); describe("automatically fixing files", () => { @@ -222,7 +222,7 @@ describe("bin/eslint.js", () => { }); it("has exit code 0 and fixes a file if all rules can be fixed", () => { - const child = runESLint(["--fix", "--no-eslintrc", "--no-ignore", tempFilePath]); + const child = runESLint(["--fix", "--no-config-lookup", "--no-ignore", tempFilePath]); const exitCodeAssertion = assertExitCode(child, 0); const outputFileAssertion = awaitExit(child).then(() => { assert.strictEqual(fs.readFileSync(tempFilePath).toString(), expectedFixedText); @@ -232,7 +232,7 @@ describe("bin/eslint.js", () => { }); it("has exit code 0, fixes errors in a file, and does not report or fix warnings if --quiet and --fix are used", () => { - const child = runESLint(["--fix", "--quiet", "--no-eslintrc", "--no-ignore", tempFilePath]); + const child = runESLint(["--fix", "--quiet", "--no-config-lookup", "--no-ignore", tempFilePath]); const exitCodeAssertion = assertExitCode(child, 0); const stdoutAssertion = getOutput(child).then(output => assert.strictEqual(output.stdout, "")); const outputFileAssertion = awaitExit(child).then(() => { @@ -243,7 +243,7 @@ describe("bin/eslint.js", () => { }); it("has exit code 1 and fixes a file if not all rules can be fixed", () => { - const child = runESLint(["--fix", "--no-eslintrc", "--no-ignore", "--rule", "max-len: [2, 10]", tempFilePath]); + const child = runESLint(["--fix", "--no-config-lookup", "--no-ignore", "--rule", "max-len: [2, 10]", tempFilePath]); const exitCodeAssertion = assertExitCode(child, 1); const outputFileAssertion = awaitExit(child).then(() => { assert.strictEqual(fs.readFileSync(tempFilePath).toString(), expectedFixedText); @@ -260,7 +260,7 @@ describe("bin/eslint.js", () => { describe("cache files", () => { const CACHE_PATH = ".temp-eslintcache"; const SOURCE_PATH = "tests/fixtures/cache/src/test-file.js"; - const ARGS_WITHOUT_CACHE = ["--no-eslintrc", "--no-ignore", SOURCE_PATH, "--cache-location", CACHE_PATH]; + const ARGS_WITHOUT_CACHE = ["--no-config-lookup", "--no-ignore", SOURCE_PATH, "--cache-location", CACHE_PATH]; const ARGS_WITH_CACHE = ARGS_WITHOUT_CACHE.concat("--cache"); describe("when no cache file exists", () => { @@ -388,40 +388,10 @@ describe("bin/eslint.js", () => { }); it("prints the error message pointing to line of code", () => { - const invalidConfig = path.join(__dirname, "../fixtures/bin/.eslintrc.yml"); - const child = runESLint(["--no-ignore", invalidConfig]); - const exitCodeAssertion = assertExitCode(child, 2); - const outputAssertion = getOutput(child).then(output => { - assert.strictEqual(output.stdout, ""); - assert.match( - output.stderr, - /: bad indentation of a mapping entry \(\d+:\d+\)/u // a part of the error message from `js-yaml` dependency - ); - }); - - return Promise.all([exitCodeAssertion, outputAssertion]); - }); - }); - + const invalidConfig = path.join(__dirname, "../fixtures/bin/eslint.config.js"); + const child = runESLint(["--no-ignore", "-c", invalidConfig]); - describe("emitting a warning for ecmaFeatures", () => { - it("does not emit a warning when it does not find an ecmaFeatures option", () => { - const child = runESLint(["Makefile.js"]); - - const exitCodePromise = assertExitCode(child, 0); - const outputPromise = getOutput(child).then(output => assert.strictEqual(output.stderr, "")); - - return Promise.all([exitCodePromise, outputPromise]); - }); - it("emits a warning when it finds an ecmaFeatures option", () => { - const child = runESLint(["-c", "tests/fixtures/config-file/ecma-features/.eslintrc.yml", "Makefile.js"]); - - const exitCodePromise = assertExitCode(child, 0); - const outputPromise = getOutput(child).then(output => { - assert.include(output.stderr, "The 'ecmaFeatures' config file property is deprecated and has no effect."); - }); - - return Promise.all([exitCodePromise, outputPromise]); + return assertExitCode(child, 2); }); }); diff --git a/tests/fixtures/bin/eslint.config.js b/tests/fixtures/bin/eslint.config.js new file mode 100644 index 00000000000..bbaa7590f3f --- /dev/null +++ b/tests/fixtures/bin/eslint.config.js @@ -0,0 +1,5 @@ +// intentionally invalid JavaScript +rules: + semi: error +yoda: error + quotes: error diff --git a/tests/fixtures/configurations/es6.js b/tests/fixtures/configurations/es6.js new file mode 100644 index 00000000000..975beb20920 --- /dev/null +++ b/tests/fixtures/configurations/es6.js @@ -0,0 +1,7 @@ +module.exports = { + languageOptions: { + parserOptions: { + ecmaVersion: 6 + } + } +}; diff --git a/tests/fixtures/max-warnings/eslint.config.js b/tests/fixtures/max-warnings/eslint.config.js new file mode 100644 index 00000000000..8ad137c75fa --- /dev/null +++ b/tests/fixtures/max-warnings/eslint.config.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + quotes: [1, "single"] + } +}; diff --git a/tests/lib/cli.js b/tests/lib/cli.js index d1725c05b72..60fdac64602 100644 --- a/tests/lib/cli.js +++ b/tests/lib/cli.js @@ -49,9 +49,10 @@ describe("cli", () => { * Verify that ESLint class receives correct opts via await cli.execute(). * @param {string} cmd CLI command. * @param {Object} opts Options hash that should match that received by ESLint class. + * @param {string} configType The config type to work with. * @returns {void} */ - async function verifyESLintOpts(cmd, opts) { + async function verifyESLintOpts(cmd, opts, configType) { // create a fake ESLint class to test with const fakeESLint = sinon.mock().withExactArgs(sinon.match(opts)); @@ -62,10 +63,11 @@ describe("cli", () => { const localCLI = proxyquire("../../lib/cli", { "./eslint": { ESLint: fakeESLint }, + "./flat-eslint": { FlatESLint: fakeESLint, findFlatConfigFile: () => null }, "./shared/logging": log }); - await localCLI.execute(cmd); + await localCLI.execute(cmd, null, configType === "flat"); sinon.verifyAndRestore(); } @@ -105,1123 +107,1221 @@ describe("cli", () => { sh.rm("-r", fixtureDir); }); - describe("execute()", () => { - it("should return error when text with incorrect quotes is passed as argument", async () => { - const configFile = getFixturePath("configurations", "quotes-error.json"); - const result = await cli.execute(`-c ${configFile}`, "var foo = 'bar';"); + ["eslintrc", "flat"].forEach(configType => { - assert.strictEqual(result, 1); - }); - - it("should not print debug info when passed the empty string as text", async () => { - const result = await cli.execute(["--stdin", "--no-eslintrc"], ""); + const useFlatConfig = configType === "flat"; - assert.strictEqual(result, 0); - assert.isTrue(log.info.notCalled); - }); + describe("execute()", () => { - it("should return no error when --ext .js2 is specified", async () => { - const filePath = getFixturePath("files"); - const result = await cli.execute(`--ext .js2 ${filePath}`); + it(`should return error when text with incorrect quotes is passed as argument with configType:${configType}`, async () => { + const configFile = getFixturePath("configurations", "quotes-error.js"); + const result = await cli.execute(`-c ${configFile} --stdin --stdin-filename foo.js`, "var foo = 'bar';", useFlatConfig); - assert.strictEqual(result, 0); - }); + assert.strictEqual(result, 1); + }); - it("should exit with console error when passed unsupported arguments", async () => { - const filePath = getFixturePath("files"); - const result = await cli.execute(`--blah --another ${filePath}`); + it(`should not print debug info when passed the empty string as text with configType:${configType}`, async () => { + const flag = useFlatConfig ? "--no-config-lookup" : "--no-eslintrc"; + const result = await cli.execute(["--stdin", flag, "--stdin-filename", "foo.js"], "", useFlatConfig); - assert.strictEqual(result, 2); - }); + assert.strictEqual(result, 0); + assert.isTrue(log.info.notCalled); + }); - }); + it(`should exit with console error when passed unsupported arguments with configType:${configType}`, async () => { + const filePath = getFixturePath("files"); + const result = await cli.execute(`--blah --another ${filePath}`, null, useFlatConfig); - describe("when given a config file", () => { - it("should load the specified config file", async () => { - const configPath = getFixturePath(".eslintrc"); - const filePath = getFixturePath("passing.js"); + assert.strictEqual(result, 2); + }); - await cli.execute(`--config ${configPath} ${filePath}`); }); - }); - describe("when there is a local config file", () => { - const code = "lib/cli.js"; + describe("when given a config with rules with options and severity level set to error", () => { - it("should load the local config file", async () => { + const originalCwd = process.cwd; - // Mock CWD - process.eslintCwd = getFixturePath("configurations", "single-quotes"); + beforeEach(() => { + process.cwd = () => getFixturePath(); + }); - await cli.execute(code); + afterEach(() => { + process.cwd = originalCwd; + }); - process.eslintCwd = null; + it(`should exit with an error status (1) with configType:${configType}`, async () => { + const configPath = getFixturePath("configurations", "quotes-error.js"); + const filePath = getFixturePath("single-quoted.js"); + const code = `--no-ignore --config ${configPath} ${filePath}`; + + const exitStatus = await cli.execute(code, null, useFlatConfig); + + assert.strictEqual(exitStatus, 1); + }); }); - }); - describe("when given a config with rules with options and severity level set to error", () => { - it("should exit with an error status (1)", async () => { - const configPath = getFixturePath("configurations", "quotes-error.json"); - const filePath = getFixturePath("single-quoted.js"); - const code = `--no-ignore --config ${configPath} ${filePath}`; + describe("when given a config file and a directory of files", () => { + it(`should load and execute without error with configType:${configType}`, async () => { + const configPath = getFixturePath("configurations", "semi-error.js"); + const filePath = getFixturePath("formatters"); + const code = `--config ${configPath} ${filePath}`; + const exitStatus = await cli.execute(code, null, useFlatConfig); - const exitStatus = await cli.execute(code); + assert.strictEqual(exitStatus, 0); + }); + }); - assert.strictEqual(exitStatus, 1); + describe("when there is a local config file", () => { + const code = "lib/cli.js"; + + it(`should load the local config file with configType:${configType}`, async () => { + await cli.execute(code, null, useFlatConfig); + }); }); - }); - describe("when given a config file and a directory of files", () => { - it("should load and execute without error", async () => { - const configPath = getFixturePath("configurations", "semi-error.json"); - const filePath = getFixturePath("formatters"); - const code = `--config ${configPath} ${filePath}`; + describe("Formatters", () => { - const exitStatus = await cli.execute(code); + describe("when given a valid built-in formatter name", () => { + it(`should execute without any errors with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const flag = useFlatConfig ? "--no-config-lookup" : "--no-eslintrc"; + const exit = await cli.execute(`${flag} -f checkstyle ${filePath}`, null, useFlatConfig); - assert.strictEqual(exitStatus, 0); - }); - }); + assert.strictEqual(exit, 0); + }); + }); - describe("when given a config with environment set to browser", () => { - it("should execute without any errors", async () => { - const configPath = getFixturePath("configurations", "env-browser.json"); - const filePath = getFixturePath("globals-browser.js"); - const code = `--config ${configPath} ${filePath}`; + describe("when given a valid built-in formatter name that uses rules meta.", () => { + + const originalCwd = process.cwd; + + beforeEach(() => { + process.cwd = () => getFixturePath(); + }); + + afterEach(() => { + process.cwd = originalCwd; + }); + + it(`should execute without any errors with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const flag = useFlatConfig ? "--no-config-lookup" : "--no-eslintrc"; + const exit = await cli.execute(`--no-ignore -f json-with-metadata ${filePath} ${flag}`, null, useFlatConfig); + + assert.strictEqual(exit, 0); + + /* + * Note: There is a behavior difference between eslintrc and flat config + * when using formatters. For eslintrc, rulesMeta always contains every + * rule that was loaded during the last run; for flat config, rulesMeta + * only contains meta data for the rules that triggered messages in the + * results. (Flat config uses ESLint#getRulesMetaForResults().) + */ + + // Check metadata. + const { metadata } = JSON.parse(log.info.args[0][0]); + const expectedMetadata = { + cwd: process.cwd(), + rulesMeta: useFlatConfig ? {} : Array.from(BuiltinRules).reduce((obj, [ruleId, rule]) => { + obj[ruleId] = rule.meta; + return obj; + }, {}) + }; + + assert.deepStrictEqual(metadata, expectedMetadata); + }); + }); - const exit = await cli.execute(code); + describe("when given an invalid built-in formatter name", () => { + it(`should execute with error: with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`-f fakeformatter ${filePath}`); - assert.strictEqual(exit, 0); - }); - }); + assert.strictEqual(exit, 2); + }); + }); - describe("when given a config with environment set to Node.js", () => { - it("should execute without any errors", async () => { - const configPath = getFixturePath("configurations", "env-node.json"); - const filePath = getFixturePath("globals-node.js"); - const code = `--config ${configPath} ${filePath}`; + describe("when given a valid formatter path", () => { + it(`should execute without any errors with configType:${configType}`, async () => { + const formatterPath = getFixturePath("formatters", "simple.js"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); - const exit = await cli.execute(code); + assert.strictEqual(exit, 0); + }); + }); - assert.strictEqual(exit, 0); - }); - }); + describe("when given an invalid formatter path", () => { + it(`should execute with error with configType:${configType}`, async () => { + const formatterPath = getFixturePath("formatters", "file-does-not-exist.js"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`, null, useFlatConfig); - describe("when given a config with environment set to Nashorn", () => { - it("should execute without any errors", async () => { - const configPath = getFixturePath("configurations", "env-nashorn.json"); - const filePath = getFixturePath("globals-nashorn.js"); - const code = `--config ${configPath} ${filePath}`; + assert.strictEqual(exit, 2); + }); + }); - const exit = await cli.execute(code); + describe("when given an async formatter path", () => { + it(`should execute without any errors with configType:${configType}`, async () => { + const formatterPath = getFixturePath("formatters", "async.js"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); - assert.strictEqual(exit, 0); + assert.strictEqual(log.info.getCall(0).args[0], "from async formatter"); + assert.strictEqual(exit, 0); + }); + }); }); - }); - describe("when given a config with environment set to WebExtensions", () => { - it("should execute without any errors", async () => { - const configPath = getFixturePath("configurations", "env-webextensions.json"); - const filePath = getFixturePath("globals-webextensions.js"); - const code = `--config ${configPath} ${filePath}`; + describe("Exit Codes", () => { - const exit = await cli.execute(code); + const originalCwd = process.cwd; - assert.strictEqual(exit, 0); - }); - }); + beforeEach(() => { + process.cwd = () => getFixturePath(); + }); - describe("when given a valid built-in formatter name", () => { - it("should execute without any errors", async () => { - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`-f checkstyle ${filePath}`); + afterEach(() => { + process.cwd = originalCwd; + }); - assert.strictEqual(exit, 0); - }); - }); + describe("when executing a file with a lint error", () => { - describe("when given a valid built-in formatter name that uses rules meta.", () => { - it("should execute without any errors", async () => { - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`-f json-with-metadata ${filePath} --no-eslintrc`); + it(`should exit with error with configType:${configType}`, async () => { + const filePath = getFixturePath("undef.js"); + const code = `--no-ignore --rule no-undef:2 ${filePath}`; - assert.strictEqual(exit, 0); + const exit = await cli.execute(code, null, useFlatConfig); - // Check metadata. - const { metadata } = JSON.parse(log.info.args[0][0]); - const expectedMetadata = { - cwd: process.cwd(), - rulesMeta: Array.from(BuiltinRules).reduce((obj, [ruleId, rule]) => { - obj[ruleId] = rule.meta; - return obj; - }, {}) - }; + assert.strictEqual(exit, 1); + }); + }); - assert.deepStrictEqual(metadata, expectedMetadata); - }); - }); + describe("when using --fix-type without --fix or --fix-dry-run", () => { + it(`should exit with error with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const code = `--fix-type suggestion ${filePath}`; - describe("when given an invalid built-in formatter name", () => { - it("should execute with error", async () => { - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`-f fakeformatter ${filePath}`); + const exit = await cli.execute(code, null, useFlatConfig); - assert.strictEqual(exit, 2); - }); - }); + assert.strictEqual(exit, 2); + }); + }); - describe("when given a valid formatter path", () => { - it("should execute without any errors", async () => { - const formatterPath = getFixturePath("formatters", "simple.js"); - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); + describe("when executing a file with a syntax error", () => { + it(`should exit with error with configType:${configType}`, async () => { + const filePath = getFixturePath("syntax-error.js"); + const exit = await cli.execute(`--no-ignore ${filePath}`, null, useFlatConfig); + + assert.strictEqual(exit, 1); + }); + }); - assert.strictEqual(exit, 0); }); - }); - describe("when given an invalid formatter path", () => { - it("should execute with error", async () => { - const formatterPath = getFixturePath("formatters", "file-does-not-exist.js"); - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); + describe("when calling execute more than once", () => { - assert.strictEqual(exit, 2); - }); - }); + const originalCwd = process.cwd; - describe("when given an async formatter path", () => { - it("should execute without any errors", async () => { - const formatterPath = getFixturePath("formatters", "async.js"); - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); + beforeEach(() => { + process.cwd = () => getFixturePath(); + }); - assert.strictEqual(log.info.getCall(0).args[0], "from async formatter"); - assert.strictEqual(exit, 0); - }); - }); + afterEach(() => { + process.cwd = originalCwd; + }); - describe("when executing a file with a lint error", () => { - it("should exit with error", async () => { - const filePath = getFixturePath("undef.js"); - const code = `--no-ignore --rule no-undef:2 ${filePath}`; + it(`should not print the results from previous execution with configType:${configType}`, async () => { + const filePath = getFixturePath("missing-semicolon.js"); + const passingPath = getFixturePath("passing.js"); - const exit = await cli.execute(code); + await cli.execute(`--no-ignore --rule semi:2 ${filePath}`, null, useFlatConfig); - assert.strictEqual(exit, 1); - }); - }); + assert.isTrue(log.info.called, "Log should have been called."); - describe("when using --fix-type without --fix or --fix-dry-run", () => { - it("should exit with error", async () => { - const filePath = getFixturePath("passing.js"); - const code = `--fix-type suggestion ${filePath}`; + log.info.resetHistory(); - const exit = await cli.execute(code); + await cli.execute(`--no-ignore --rule semi:2 ${passingPath}`, null, useFlatConfig); + assert.isTrue(log.info.notCalled); - assert.strictEqual(exit, 2); + }); }); - }); - describe("when executing a file with a syntax error", () => { - it("should exit with error", async () => { - const filePath = getFixturePath("syntax-error.js"); - const exit = await cli.execute(`--no-ignore ${filePath}`); - - assert.strictEqual(exit, 1); + describe("when executing with version flag", () => { + it(`should print out current version with configType:${configType}`, async () => { + assert.strictEqual(await cli.execute("-v", null, useFlatConfig), 0); + assert.strictEqual(log.info.callCount, 1); + }); }); - }); - describe("when calling execute more than once", () => { - it("should not print the results from previous execution", async () => { - const filePath = getFixturePath("missing-semicolon.js"); - const passingPath = getFixturePath("passing.js"); + describe("when executing with env-info flag", () => { - await cli.execute(`--no-ignore --rule semi:2 ${filePath}`); + it(`should print out environment information with configType:${configType}`, async () => { + assert.strictEqual(await cli.execute("--env-info", null, useFlatConfig), 0); + assert.strictEqual(log.info.callCount, 1); + }); - assert.isTrue(log.info.called, "Log should have been called."); + describe("With error condition", () => { - log.info.resetHistory(); + beforeEach(() => { + RuntimeInfo.environment = sinon.stub().throws("There was an error!"); + }); - await cli.execute(`--no-ignore --rule semi:2 ${passingPath}`); - assert.isTrue(log.info.notCalled); + afterEach(() => { + RuntimeInfo.environment = sinon.stub(); + }); - }); - }); + it(`should print error message and return error code with configType:${configType}`, async () => { - describe("when executing with version flag", () => { - it("should print out current version", async () => { - assert.strictEqual(await cli.execute("-v"), 0); - assert.strictEqual(log.info.callCount, 1); - }); - }); + assert.strictEqual(await cli.execute("--env-info", null, useFlatConfig), 2); + assert.strictEqual(log.error.callCount, 1); + }); + }); - describe("when executing with env-info flag", () => { - it("should print out environment information", async () => { - assert.strictEqual(await cli.execute("--env-info"), 0); - assert.strictEqual(log.info.callCount, 1); }); - it("should print error message and return error code", async () => { - RuntimeInfo.environment.throws("There was an error!"); - - assert.strictEqual(await cli.execute("--env-info"), 2); - assert.strictEqual(log.error.callCount, 1); + describe("when executing with help flag", () => { + it(`should print out help with configType:${configType}`, async () => { + assert.strictEqual(await cli.execute("-h", null, useFlatConfig), 0); + assert.strictEqual(log.info.callCount, 1); + }); }); - }); - describe("when executing without no-error-on-unmatched-pattern flag", () => { - it("should throw an error on unmatched glob pattern", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const globPattern = "*.js3"; + describe("when executing a file with a shebang", () => { + it(`should execute without error with configType:${configType}`, async () => { + const filePath = getFixturePath("shebang.js"); + const flag = useFlatConfig ? "--no-config-lookup" : "--no-eslintrc"; + const exit = await cli.execute(`${flag} --no-ignore ${filePath}`, null, useFlatConfig); - await stdAssert.rejects(async () => { - await cli.execute(`"${filePath}/${globPattern}"`); - }, new Error(`No files matching '${filePath}/${globPattern}' were found.`)); + assert.strictEqual(exit, 0); + }); }); - it("should throw an error on unmatched --ext", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const extension = ".js3"; + describe("FixtureDir Dependent Tests", () => { - await stdAssert.rejects(async () => { - await cli.execute(`--ext ${extension} ${filePath}`); - }, `No files matching '${filePath}' were found`); - }); - }); + const originalCwd = process.cwd; - describe("when executing with no-error-on-unmatched-pattern flag", () => { - it("should not throw an error on unmatched node glob syntax patterns", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const exit = await cli.execute(`--no-error-on-unmatched-pattern "${filePath}/*.js3"`); + beforeEach(() => { + process.cwd = () => getFixturePath(); + }); - assert.strictEqual(exit, 0); - }); + afterEach(() => { + process.cwd = originalCwd; + }); - it("should not throw an error on unmatched --ext", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 ${filePath}`); + describe("when executing with global flag", () => { - assert.strictEqual(exit, 0); - }); - }); + it(`should default defined variables to read-only with configType:${configType}`, async () => { + const filePath = getFixturePath("undef.js"); + const exit = await cli.execute(`--global baz,bat --no-ignore --rule no-global-assign:2 ${filePath}`, null, useFlatConfig); - describe("when executing with no-error-on-unmatched-pattern flag and multiple patterns", () => { - it("should not throw an error on multiple unmatched node glob syntax patterns", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js4`); + assert.isTrue(log.info.calledOnce); + assert.strictEqual(exit, 1); + }); - assert.strictEqual(exit, 0); - }); + it(`should allow defining writable global variables with configType:${configType}`, async () => { + const filePath = getFixturePath("undef.js"); + const exit = await cli.execute(`--global baz:false,bat:true --no-ignore ${filePath}`, null, useFlatConfig); - it("should still throw an error on when a matched pattern has lint errors", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js`); + assert.isTrue(log.info.notCalled); + assert.strictEqual(exit, 0); + }); - assert.strictEqual(exit, 1); - }); - }); + it(`should allow defining variables with multiple flags with configType:${configType}`, async () => { + const filePath = getFixturePath("undef.js"); + const exit = await cli.execute(`--global baz --global bat:true --no-ignore ${filePath}`); - describe("when executing with no-error-on-unmatched-pattern flag and multiple --ext arguments", () => { - it("should not throw an error on multiple unmatched --ext arguments", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js4 ${filePath}`); + assert.isTrue(log.info.notCalled); + assert.strictEqual(exit, 0); + }); + }); - assert.strictEqual(exit, 0); - }); - it("should still throw an error on when a matched pattern has lint errors", async () => { - const filePath = getFixturePath("unmatched-patterns"); - const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js ${filePath}`); + describe("when supplied with rule flag and severity level set to error", () => { - assert.strictEqual(exit, 1); - }); - }); - describe("when executing with help flag", () => { - it("should print out help", async () => { - assert.strictEqual(await cli.execute("-h"), 0); - assert.strictEqual(log.info.callCount, 1); - }); - }); + it(`should exit with an error status (2) with configType:${configType}`, async () => { + const filePath = getFixturePath("single-quoted.js"); + const code = `--no-ignore --rule 'quotes: [2, double]' ${filePath}`; + const exitStatus = await cli.execute(code, null, useFlatConfig); - describe("when given a directory with eslint excluded files in the directory", () => { - it("should throw an error and not process any files", async () => { - const ignorePath = getFixturePath(".eslintignore"); - const filePath = getFixturePath("cli"); + assert.strictEqual(exitStatus, 1); + }); + }); - await stdAssert.rejects(async () => { - await cli.execute(`--ignore-path ${ignorePath} ${filePath}`); - }, new Error(`All files matched by '${filePath}' are ignored.`)); - }); - }); + describe("when the quiet option is enabled", () => { - describe("when given a file in excluded files list", () => { - it("should not process the file", async () => { - const ignorePath = getFixturePath(".eslintignore"); - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`--ignore-path ${ignorePath} ${filePath}`); + it(`should only print error with configType:${configType}`, async () => { + const filePath = getFixturePath("single-quoted.js"); + const cliArgs = `--no-ignore --quiet -f compact --rule 'quotes: [2, double]' --rule 'no-unused-vars: 1' ${filePath}`; - // a warning about the ignored file - assert.isTrue(log.info.called); - assert.strictEqual(exit, 0); - }); + await cli.execute(cliArgs, null, useFlatConfig); - it("should process the file when forced", async () => { - const ignorePath = getFixturePath(".eslintignore"); - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`--ignore-path ${ignorePath} --no-ignore ${filePath}`); + sinon.assert.calledOnce(log.info); - // no warnings - assert.isFalse(log.info.called); - assert.strictEqual(exit, 0); - }); - }); + const formattedOutput = log.info.firstCall.args[0]; - describe("when given a pattern to ignore", () => { - it("should not process any files", async () => { - const ignoredFile = getFixturePath("cli/syntax-error.js"); - const filePath = getFixturePath("cli/passing.js"); - const exit = await cli.execute(`--ignore-pattern cli/ ${ignoredFile} ${filePath}`); + assert.include(formattedOutput, "Error"); + assert.notInclude(formattedOutput, "Warning"); + }); - // warnings about the ignored files - assert.isTrue(log.info.called); - assert.strictEqual(exit, 0); - }); - }); + it(`should print nothing if there are no errors with configType:${configType}`, async () => { + const filePath = getFixturePath("single-quoted.js"); + const cliArgs = `--quiet -f compact --rule 'quotes: [1, double]' --rule 'no-unused-vars: 1' ${filePath}`; - describe("when given patterns to ignore", () => { - it("should not process any matching files", async () => { - const ignorePaths = ["a", "b"]; + await cli.execute(cliArgs, null, useFlatConfig); - const cmd = ignorePaths.map(ignorePath => `--ignore-pattern ${ignorePath}`).concat(".").join(" "); + sinon.assert.notCalled(log.info); + }); + }); - const opts = { - overrideConfig: { - ignorePatterns: ignorePaths - } - }; - await verifyESLintOpts(cmd, opts); - }); - }); + describe("no-error-on-unmatched-pattern flag", () => { - describe("when executing a file with a shebang", () => { - it("should execute without error", async () => { - const filePath = getFixturePath("shebang.js"); - const exit = await cli.execute(`--no-ignore ${filePath}`); + describe("when executing without no-error-on-unmatched-pattern flag", () => { + it(`should throw an error on unmatched glob pattern with configType:${configType}`, async () => { + const filePath = getFixturePath("unmatched-patterns"); + const globPattern = "unmatched*.js"; - assert.strictEqual(exit, 0); - }); - }); + await stdAssert.rejects(async () => { + await cli.execute(`"${filePath}/${globPattern}"`, null, useFlatConfig); + }, new Error(`No files matching '${filePath}/${globPattern}' were found.`)); + }); - describe("when loading a custom rule", () => { - it("should return an error when rule isn't found", async () => { - const rulesPath = getFixturePath("rules", "wrong"); - const configPath = getFixturePath("rules", "eslint.json"); - const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); - const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; + }); - await stdAssert.rejects(async () => { - const exit = await cli.execute(code); + describe("when executing with no-error-on-unmatched-pattern flag", () => { + it(`should not throw an error on unmatched node glob syntax patterns with configType:${configType}`, async () => { + const filePath = getFixturePath("unmatched-patterns"); + const exit = await cli.execute(`--no-error-on-unmatched-pattern "${filePath}/unmatched*.js"`, null, useFlatConfig); - assert.strictEqual(exit, 2); - }, /Error while loading rule 'custom-rule': Boom!/u); - }); + assert.strictEqual(exit, 0); + }); + }); - it("should return a warning when rule is matched", async () => { - const rulesPath = getFixturePath("rules"); - const configPath = getFixturePath("rules", "eslint.json"); - const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); - const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; + describe("when executing with no-error-on-unmatched-pattern flag and multiple patterns", () => { + it(`should not throw an error on multiple unmatched node glob syntax patterns with configType:${configType}`, async () => { + const filePath = getFixturePath("unmatched-patterns/js3"); + const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/unmatched1*.js ${filePath}/unmatched2*.js`, null, useFlatConfig); - await cli.execute(code); + assert.strictEqual(exit, 0); + }); - assert.isTrue(log.info.calledOnce); - assert.isTrue(log.info.neverCalledWith("")); - }); + it(`should still throw an error on when a matched pattern has lint errors with configType:${configType}`, async () => { + const filePath = getFixturePath("unmatched-patterns"); + const exit = await cli.execute(`--no-ignore --no-error-on-unmatched-pattern ${filePath}/unmatched1*.js ${filePath}/failing.js`, null, useFlatConfig); - it("should return warnings from multiple rules in different directories", async () => { - const rulesPath = getFixturePath("rules", "dir1"); - const rulesPath2 = getFixturePath("rules", "dir2"); - const configPath = getFixturePath("rules", "multi-rulesdirs.json"); - const filePath = getFixturePath("rules", "test-multi-rulesdirs.js"); - const code = `--rulesdir ${rulesPath} --rulesdir ${rulesPath2} --config ${configPath} --no-ignore ${filePath}`; - const exit = await cli.execute(code); - - const call = log.info.getCall(0); - - assert.isTrue(log.info.calledOnce); - assert.isTrue(call.args[0].includes("String!")); - assert.isTrue(call.args[0].includes("Literal!")); - assert.isTrue(call.args[0].includes("2 problems")); - assert.isTrue(log.info.neverCalledWith("")); - assert.strictEqual(exit, 1); - }); + assert.strictEqual(exit, 1); + }); + }); + }); - }); + describe("Parser Options", () => { - describe("when executing with no-eslintrc flag", () => { - it("should ignore a local config file", async () => { - const filePath = getFixturePath("eslintrc", "quotes.js"); - const exit = await cli.execute(`--no-eslintrc --no-ignore ${filePath}`); + describe("when given parser options", () => { + it(`should exit with error if parser options are invalid with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`--no-ignore --parser-options test111 ${filePath}`, null, useFlatConfig); - assert.isTrue(log.info.notCalled); - assert.strictEqual(exit, 0); - }); - }); + assert.strictEqual(exit, 2); + }); - describe("when executing without no-eslintrc flag", () => { - it("should load a local config file", async () => { - const filePath = getFixturePath("eslintrc", "quotes.js"); - const exit = await cli.execute(`--no-ignore ${filePath}`); + it(`should exit with no error if parser is valid with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`, null, useFlatConfig); - assert.isTrue(log.info.calledOnce); - assert.strictEqual(exit, 1); - }); - }); + assert.strictEqual(exit, 0); + }); - describe("when executing without env flag", () => { - it("should not define environment-specific globals", async () => { - const files = [ - getFixturePath("globals-browser.js"), - getFixturePath("globals-node.js") - ]; + it(`should exit with an error on ecmaVersion 7 feature in ecmaVersion 6 with configType:${configType}`, async () => { + const filePath = getFixturePath("passing-es7.js"); + const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`, null, useFlatConfig); - await cli.execute(`--no-eslintrc --config ./conf/eslint-recommended.js --no-ignore ${files.join(" ")}`); + assert.strictEqual(exit, 1); + }); - assert.strictEqual(log.info.args[0][0].split("\n").length, 10); - }); - }); + it(`should exit with no error on ecmaVersion 7 feature in ecmaVersion 7 with configType:${configType}`, async () => { + const filePath = getFixturePath("passing-es7.js"); + const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:7 ${filePath}`, null, useFlatConfig); - describe("when executing with global flag", () => { - it("should default defined variables to read-only", async () => { - const filePath = getFixturePath("undef.js"); - const exit = await cli.execute(`--global baz,bat --no-ignore --rule no-global-assign:2 ${filePath}`); + assert.strictEqual(exit, 0); + }); - assert.isTrue(log.info.calledOnce); - assert.strictEqual(exit, 1); - }); + it(`should exit with no error on ecmaVersion 7 feature with config ecmaVersion 6 and command line ecmaVersion 7 with configType:${configType}`, async () => { + const configPath = useFlatConfig + ? getFixturePath("configurations", "es6.js") + : getFixturePath("configurations", "es6.json"); + const filePath = getFixturePath("passing-es7.js"); + const exit = await cli.execute(`--no-ignore --config ${configPath} --parser-options=ecmaVersion:7 ${filePath}`, null, useFlatConfig); - it("should allow defining writable global variables", async () => { - const filePath = getFixturePath("undef.js"); - const exit = await cli.execute(`--global baz:false,bat:true --no-ignore ${filePath}`); + assert.strictEqual(exit, 0); + }); + }); + }); - assert.isTrue(log.info.notCalled); - assert.strictEqual(exit, 0); - }); + describe("when given the max-warnings flag", () => { - it("should allow defining variables with multiple flags", async () => { - const filePath = getFixturePath("undef.js"); - const exit = await cli.execute(`--global baz --global bat:true --no-ignore ${filePath}`); + let filePath, configFilePath; - assert.isTrue(log.info.notCalled); - assert.strictEqual(exit, 0); - }); - }); + before(() => { + filePath = getFixturePath("max-warnings/six-warnings.js"); + configFilePath = getFixturePath(useFlatConfig ? "max-warnings/eslint.config.js" : "max-warnings/.eslintrc"); + }); - describe("when supplied with rule flag and severity level set to error", () => { - it("should exit with an error status (2)", async () => { - const filePath = getFixturePath("single-quoted.js"); - const code = `--no-ignore --rule 'quotes: [2, double]' ${filePath}`; - const exitStatus = await cli.execute(code); + it(`should not change exit code if warning count under threshold with configType:${configType}`, async () => { + const exitCode = await cli.execute(`--no-ignore --max-warnings 10 ${filePath} -c ${configFilePath}`, null, useFlatConfig); - assert.strictEqual(exitStatus, 1); - }); - }); + assert.strictEqual(exitCode, 0); + }); - describe("when the quiet option is enabled", () => { + it(`should exit with exit code 1 if warning count exceeds threshold with configType:${configType}`, async () => { + const exitCode = await cli.execute(`--no-ignore --max-warnings 5 ${filePath} -c ${configFilePath}`, null, useFlatConfig); - it("should only print error", async () => { - const filePath = getFixturePath("single-quoted.js"); - const cliArgs = `--no-ignore --quiet -f compact --rule 'quotes: [2, double]' --rule 'no-unused-vars: 1' ${filePath}`; + assert.strictEqual(exitCode, 1); + assert.ok(log.error.calledOnce); + assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); + }); - await cli.execute(cliArgs); + it(`should exit with exit code 1 without printing warnings if the quiet option is enabled and warning count exceeds threshold with configType:${configType}`, async () => { + const exitCode = await cli.execute(`--no-ignore --quiet --max-warnings 5 ${filePath} -c ${configFilePath}`, null, useFlatConfig); - sinon.assert.calledOnce(log.info); + assert.strictEqual(exitCode, 1); + assert.ok(log.error.calledOnce); + assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); + assert.ok(log.info.notCalled); // didn't print warnings + }); - const formattedOutput = log.info.firstCall.args[0]; + it(`should not change exit code if warning count equals threshold with configType:${configType}`, async () => { + const exitCode = await cli.execute(`--no-ignore --max-warnings 6 ${filePath} -c ${configFilePath}`, null, useFlatConfig); - assert.include(formattedOutput, "Error"); - assert.notInclude(formattedOutput, "Warning"); - }); + assert.strictEqual(exitCode, 0); + }); - it("should print nothing if there are no errors", async () => { - const filePath = getFixturePath("single-quoted.js"); - const cliArgs = `--quiet -f compact --rule 'quotes: [1, double]' --rule 'no-unused-vars: 1' ${filePath}`; + it(`should not change exit code if flag is not specified and there are warnings with configType:${configType}`, async () => { + const exitCode = await cli.execute(`-c ${configFilePath} ${filePath}`, null, useFlatConfig); - await cli.execute(cliArgs); + assert.strictEqual(exitCode, 0); + }); + }); - sinon.assert.notCalled(log.info); - }); - }); + describe("when given the exit-on-fatal-error flag", () => { + it(`should not change exit code if no fatal errors are reported with configType:${configType}`, async () => { + const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error.js"); + const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`, null, useFlatConfig); - describe("when supplied with report output file path", () => { - afterEach(() => { - sh.rm("-rf", "tests/output"); - }); + assert.strictEqual(exitCode, 0); + }); - it("should write the file and create dirs if they don't exist", async () => { - const filePath = getFixturePath("single-quoted.js"); - const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; + it(`should exit with exit code 1 if no fatal errors are found, but rule violations are found with configType:${configType}`, async () => { + const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error-rule-violation.js"); + const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`, null, useFlatConfig); - await cli.execute(code); + assert.strictEqual(exitCode, 1); + }); - assert.include(fs.readFileSync("tests/output/eslint-output.txt", "utf8"), filePath); - assert.isTrue(log.info.notCalled); - }); + it(`should exit with exit code 2 if fatal error is found with configType:${configType}`, async () => { + const filePath = getFixturePath("exit-on-fatal-error", "fatal-error.js"); + const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`, null, useFlatConfig); - it("should return an error if the path is a directory", async () => { - const filePath = getFixturePath("single-quoted.js"); - const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output ${filePath}`; + assert.strictEqual(exitCode, 2); + }); - fs.mkdirSync("tests/output"); + it(`should exit with exit code 2 if fatal error is found in any file with configType:${configType}`, async () => { + const filePath = getFixturePath("exit-on-fatal-error"); + const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`, null, useFlatConfig); - const exit = await cli.execute(code); + assert.strictEqual(exitCode, 2); + }); - assert.strictEqual(exit, 2); - assert.isTrue(log.info.notCalled); - assert.isTrue(log.error.calledOnce); - }); - it("should return an error if the path could not be written to", async () => { - const filePath = getFixturePath("single-quoted.js"); - const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; + }); + - fs.writeFileSync("tests/output", "foo"); + describe("Ignores", () => { + + describe("when given a directory with eslint excluded files in the directory", () => { + it(`should throw an error and not process any files with configType:${configType}`, async () => { + const ignorePath = getFixturePath(".eslintignore"); + const filePath = getFixturePath("cli"); + const expectedMessage = useFlatConfig + ? `All files matched by '${filePath.replace(/\\/gu, "/")}/**/*.js' are ignored.` + : `All files matched by '${filePath}' are ignored.`; + + await stdAssert.rejects(async () => { + await cli.execute(`--ignore-path ${ignorePath} ${filePath}`, null, useFlatConfig); + }, new Error(expectedMessage)); + }); + }); + + describe("when given a file in excluded files list", () => { + it(`should not process the file with configType:${configType}`, async () => { + const ignorePath = getFixturePath(".eslintignore"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`--ignore-path ${ignorePath} ${filePath}`, null, useFlatConfig); + + // a warning about the ignored file + assert.isTrue(log.info.called); + assert.strictEqual(exit, 0); + }); + + it(`should process the file when forced with configType:${configType}`, async () => { + const ignorePath = getFixturePath(".eslintignore"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`--ignore-path ${ignorePath} --no-ignore ${filePath}`, null, useFlatConfig); + + // no warnings + assert.isFalse(log.info.called); + assert.strictEqual(exit, 0); + }); + }); + + describe("when given a pattern to ignore", () => { + it(`should not process any files with configType:${configType}`, async () => { + const ignoredFile = getFixturePath("cli/syntax-error.js"); + const filePath = getFixturePath("cli/passing.js"); + const exit = await cli.execute(`--ignore-pattern cli/ ${ignoredFile} ${filePath}`, null, useFlatConfig); + + // warnings about the ignored files + assert.isTrue(log.info.called); + assert.strictEqual(exit, 0); + }); + }); - const exit = await cli.execute(code); + }); - assert.strictEqual(exit, 2); - assert.isTrue(log.info.notCalled); - assert.isTrue(log.error.calledOnce); }); - }); - describe("when supplied with a plugin", () => { - it("should pass plugins to ESLint", async () => { - const examplePluginName = "eslint-plugin-example"; - await verifyESLintOpts(`--no-ignore --plugin ${examplePluginName} foo.js`, { - overrideConfig: { - plugins: [examplePluginName] - } - }); - }); + describe("when given a parser name", () => { - }); + it(`should exit with a fatal error if parser is invalid with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); - describe("when supplied with a plugin-loading path", () => { - it("should pass the option to ESLint", async () => { - const examplePluginDirPath = "foo/bar"; + await stdAssert.rejects(async () => await cli.execute(`--no-ignore --parser test111 ${filePath}`, null, useFlatConfig), "Cannot find module 'test111'"); + }); + + it(`should exit with no error if parser is valid with configType:${configType}`, async () => { + const filePath = getFixturePath("passing.js"); + const flag = useFlatConfig ? "--no-config-lookup" : "--no-eslintrc"; + const exit = await cli.execute(`${flag} --no-ignore --parser espree ${filePath}`, null, useFlatConfig); - await verifyESLintOpts(`--resolve-plugins-relative-to ${examplePluginDirPath} foo.js`, { - resolvePluginsRelativeTo: examplePluginDirPath + assert.strictEqual(exit, 0); }); + }); - }); - describe("when given an parser name", () => { - it("should exit with a fatal error if parser is invalid", async () => { - const filePath = getFixturePath("passing.js"); + describe("when supplied with report output file path", () => { + afterEach(() => { + sh.rm("-rf", "tests/output"); + }); - await stdAssert.rejects(async () => await cli.execute(`--no-ignore --parser test111 ${filePath}`), "Cannot find module 'test111'"); - }); + it(`should write the file and create dirs if they don't exist with configType:${configType}`, async () => { + const filePath = getFixturePath("single-quoted.js"); + const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; - it("should exit with no error if parser is valid", async () => { - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`--no-ignore --parser espree ${filePath}`); + await cli.execute(code, null, useFlatConfig); - assert.strictEqual(exit, 0); - }); - }); + assert.include(fs.readFileSync("tests/output/eslint-output.txt", "utf8"), filePath); + assert.isTrue(log.info.notCalled); + }); - describe("when given parser options", () => { - it("should exit with error if parser options are invalid", async () => { - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`--no-ignore --parser-options test111 ${filePath}`); + it(`should return an error if the path is a directory with configType:${configType}`, async () => { + const filePath = getFixturePath("single-quoted.js"); + const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output ${filePath}`; - assert.strictEqual(exit, 2); - }); + fs.mkdirSync("tests/output"); - it("should exit with no error if parser is valid", async () => { - const filePath = getFixturePath("passing.js"); - const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); + const exit = await cli.execute(code, null, useFlatConfig); - assert.strictEqual(exit, 0); - }); + assert.strictEqual(exit, 2); + assert.isTrue(log.info.notCalled); + assert.isTrue(log.error.calledOnce); + }); - it("should exit with an error on ecmaVersion 7 feature in ecmaVersion 6", async () => { - const filePath = getFixturePath("passing-es7.js"); - const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); + it(`should return an error if the path could not be written to with configType:${configType}`, async () => { + const filePath = getFixturePath("single-quoted.js"); + const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; - assert.strictEqual(exit, 1); - }); + fs.writeFileSync("tests/output", "foo"); - it("should exit with no error on ecmaVersion 7 feature in ecmaVersion 7", async () => { - const filePath = getFixturePath("passing-es7.js"); - const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:7 ${filePath}`); + const exit = await cli.execute(code, null, useFlatConfig); - assert.strictEqual(exit, 0); + assert.strictEqual(exit, 2); + assert.isTrue(log.info.notCalled); + assert.isTrue(log.error.calledOnce); + }); }); - it("should exit with no error on ecmaVersion 7 feature with config ecmaVersion 6 and command line ecmaVersion 7", async () => { - const configPath = getFixturePath("configurations", "es6.json"); - const filePath = getFixturePath("passing-es7.js"); - const exit = await cli.execute(`--no-ignore --config ${configPath} --parser-options=ecmaVersion:7 ${filePath}`); + describe("when passed --no-inline-config", () => { + let localCLI; - assert.strictEqual(exit, 0); - }); - }); + afterEach(() => { + sinon.verifyAndRestore(); + }); - describe("when given the max-warnings flag", () => { - it("should not change exit code if warning count under threshold", async () => { - const filePath = getFixturePath("max-warnings"); - const exitCode = await cli.execute(`--no-ignore --max-warnings 10 ${filePath}`); + it(`should pass allowInlineConfig:false to ESLint when --no-inline-config is used with configType:${configType}`, async () => { + + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: false })); + + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], + errorCount: 1, + warningCount: 0 + }]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.stub(); + + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, + "./shared/logging": log + }); + + await localCLI.execute("--no-inline-config .", null, useFlatConfig); + }); - assert.strictEqual(exitCode, 0); - }); + it(`should not error and allowInlineConfig should be true by default with configType:${configType}`, async () => { - it("should exit with exit code 1 if warning count exceeds threshold", async () => { - const filePath = getFixturePath("max-warnings"); - const exitCode = await cli.execute(`--no-ignore --max-warnings 5 ${filePath}`); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: true })); - assert.strictEqual(exitCode, 1); - assert.ok(log.error.calledOnce); - assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); - }); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.stub(); - it("should exit with exit code 1 without printing warnings if the quiet option is enabled and warning count exceeds threshold", async () => { - const filePath = getFixturePath("max-warnings"); - const exitCode = await cli.execute(`--no-ignore --quiet --max-warnings 5 ${filePath}`); + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, + "./shared/logging": log + }); - assert.strictEqual(exitCode, 1); - assert.ok(log.error.calledOnce); - assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); - assert.ok(log.info.notCalled); // didn't print warnings - }); + const exitCode = await localCLI.execute(".", null, useFlatConfig); - it("should not change exit code if warning count equals threshold", async () => { - const filePath = getFixturePath("max-warnings"); - const exitCode = await cli.execute(`--no-ignore --max-warnings 6 ${filePath}`); + assert.strictEqual(exitCode, 0); + + }); - assert.strictEqual(exitCode, 0); }); - it("should not change exit code if flag is not specified and there are warnings", async () => { - const filePath = getFixturePath("max-warnings"); - const exitCode = await cli.execute(filePath); + describe("when passed --fix", () => { + let localCLI; - assert.strictEqual(exitCode, 0); - }); - }); + afterEach(() => { + sinon.verifyAndRestore(); + }); - describe("when given the exit-on-fatal-error flag", () => { - it("should not change exit code if no fatal errors are reported", async () => { - const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error.js"); - const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); + it(`should pass fix:true to ESLint when executing on files with configType:${configType}`, async () => { - assert.strictEqual(exitCode, 0); - }); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - it("should exit with exit code 1 if no fatal errors are found, but rule violations are found", async () => { - const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error-rule-violation.js"); - const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().once(); - assert.strictEqual(exitCode, 1); - }); + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, + "./shared/logging": log + }); - it("should exit with exit code 2 if fatal error is found", async () => { - const filePath = getFixturePath("exit-on-fatal-error", "fatal-error.js"); - const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); + const exitCode = await localCLI.execute("--fix .", null, useFlatConfig); - assert.strictEqual(exitCode, 2); - }); + assert.strictEqual(exitCode, 0); - it("should exit with exit code 2 if fatal error is found in any file", async () => { - const filePath = getFixturePath("exit-on-fatal-error"); - const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); + }); - assert.strictEqual(exitCode, 2); - }); + it(`should rewrite files when in fix mode with configType:${configType}`, async () => { - }); + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], + errorCount: 1, + warningCount: 0 + }]; - describe("when passed --no-inline-config", () => { - let localCLI; + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - afterEach(() => { - sinon.verifyAndRestore(); - }); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().withExactArgs(report); - it("should pass allowInlineConfig:false to ESLint when --no-inline-config is used", async () => { + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: false })); + "./shared/logging": log + }); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns([{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ], - errorCount: 1, - warningCount: 0 - }]); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.stub(); + const exitCode = await localCLI.execute("--fix .", null, useFlatConfig); + + assert.strictEqual(exitCode, 1); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log }); - await localCLI.execute("--no-inline-config ."); - }); + it(`should provide fix predicate and rewrite files when in fix mode and quiet mode with configType:${configType}`, async () => { - it("should not error and allowInlineConfig should be true by default", async () => { + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 1, + message: "Fake message" + } + ], + errorCount: 0, + warningCount: 1 + }]; - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.stub(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.getErrorResults = sinon.stub().returns([]); + fakeESLint.outputFixes = sinon.mock().withExactArgs(report); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log - }); + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - const exitCode = await localCLI.execute("."); + "./shared/logging": log + }); - assert.strictEqual(exitCode, 0); + const exitCode = await localCLI.execute("--fix --quiet .", null, useFlatConfig); - }); + assert.strictEqual(exitCode, 0); - }); + }); - describe("when passed --fix", () => { - let localCLI; + it(`should not call ESLint and return 2 when executing on text with configType:${configType}`, async () => { - afterEach(() => { - sinon.verifyAndRestore(); - }); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().never(); - it("should pass fix:true to ESLint when executing on files", async () => { + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); + "./shared/logging": log + }); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.mock().once(); + const exitCode = await localCLI.execute("--fix .", "foo = bar;", null, useFlatConfig); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + assert.strictEqual(exitCode, 2); }); - const exitCode = await localCLI.execute("--fix ."); + }); - assert.strictEqual(exitCode, 0); + describe("when passed --fix-dry-run", () => { + let localCLI; - }); + afterEach(() => { + sinon.verifyAndRestore(); + }); + it(`should pass fix:true to ESLint when executing on files with configType:${configType}`, async () => { - it("should rewrite files when in fix mode", async () => { + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - const report = [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ], - errorCount: 1, - warningCount: 0 - }]; + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().never(); + + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); + "./shared/logging": log + }); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.mock().withExactArgs(report); + const exitCode = await localCLI.execute("--fix-dry-run .", null, useFlatConfig); + + assert.strictEqual(exitCode, 0); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log }); - const exitCode = await localCLI.execute("--fix ."); + it(`should pass fixTypes to ESLint when --fix-type is passed with configType:${configType}`, async () => { - assert.strictEqual(exitCode, 1); + const expectedESLintOptions = { + fix: true, + fixTypes: ["suggestion"] + }; - }); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match(expectedESLintOptions)); - it("should provide fix predicate and rewrite files when in fix mode and quiet mode", async () => { + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.stub(); - const report = [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 1, - message: "Fake message" - } - ], - errorCount: 0, - warningCount: 1 - }]; + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); + "./shared/logging": log + }); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.getErrorResults = sinon.stub().returns([]); - fakeESLint.outputFixes = sinon.mock().withExactArgs(report); + const exitCode = await localCLI.execute("--fix-dry-run --fix-type suggestion .", null, useFlatConfig); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + assert.strictEqual(exitCode, 0); }); - const exitCode = await localCLI.execute("--fix --quiet ."); + it(`should not rewrite files when in fix-dry-run mode with configType:${configType}`, async () => { - assert.strictEqual(exitCode, 0); + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], + errorCount: 1, + warningCount: 0 + }]; - }); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); + + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().never(); + + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - it("should not call ESLint and return 2 when executing on text", async () => { + "./shared/logging": log + }); - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().never(); + const exitCode = await localCLI.execute("--fix-dry-run .", null, useFlatConfig); + + assert.strictEqual(exitCode, 1); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log }); - const exitCode = await localCLI.execute("--fix .", "foo = bar;"); + it(`should provide fix predicate when in fix-dry-run mode and quiet mode with configType:${configType}`, async () => { - assert.strictEqual(exitCode, 2); - }); + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 1, + message: "Fake message" + } + ], + errorCount: 0, + warningCount: 1 + }]; - }); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); - describe("when passed --fix-dry-run", () => { - let localCLI; + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.getErrorResults = sinon.stub().returns([]); + fakeESLint.outputFixes = sinon.mock().never(); - afterEach(() => { - sinon.verifyAndRestore(); - }); + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - it("should pass fix:true to ESLint when executing on files", async () => { + "./shared/logging": log + }); - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); + const exitCode = await localCLI.execute("--fix-dry-run --quiet .", null, useFlatConfig); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.mock().never(); + assert.strictEqual(exitCode, 0); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log }); - const exitCode = await localCLI.execute("--fix-dry-run ."); + it(`should allow executing on text with configType:${configType}`, async () => { - assert.strictEqual(exitCode, 0); + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], + errorCount: 1, + warningCount: 0 + }]; - }); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - it("should pass fixTypes to ESLint when --fix-type is passed", async () => { + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintText").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().never(); - const expectedESLintOptions = { - fix: true, - fixTypes: ["suggestion"] - }; + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match(expectedESLintOptions)); + "./shared/logging": log + }); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.stub(); + const exitCode = await localCLI.execute("--fix-dry-run .", "foo = bar;", useFlatConfig); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + assert.strictEqual(exitCode, 1); }); - const exitCode = await localCLI.execute("--fix-dry-run --fix-type suggestion ."); + it(`should not call ESLint and return 2 when used with --fix with configType:${configType}`, async () => { + + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().never(); + + localCLI = proxyquire("../../lib/cli", { + "./eslint": { ESLint: fakeESLint }, + "./eslint/flat-eslint": { ESLint: fakeESLint, findFlatConfigFile: () => null }, + + "./shared/logging": log + }); - assert.strictEqual(exitCode, 0); + const exitCode = await localCLI.execute("--fix --fix-dry-run .", "foo = bar;", useFlatConfig); + + assert.strictEqual(exitCode, 2); + }); }); - it("should not rewrite files when in fix-dry-run mode", async () => { + describe("when passing --print-config", () => { + it(`should print out the configuration with configType:${configType}`, async () => { + const filePath = getFixturePath("xxxx"); - const report = [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ], - errorCount: 1, - warningCount: 0 - }]; + const exitCode = await cli.execute(`--print-config ${filePath}`, null, useFlatConfig); + + assert.isTrue(log.info.calledOnce); + assert.strictEqual(exitCode, 0); + }); + + it(`should error if any positional file arguments are passed with configType:${configType}`, async () => { + const filePath1 = getFixturePath("files", "bar.js"); + const filePath2 = getFixturePath("files", "foo.js"); - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); + const exitCode = await cli.execute(`--print-config ${filePath1} ${filePath2}`, null, useFlatConfig); + + assert.isTrue(log.info.notCalled); + assert.isTrue(log.error.calledOnce); + assert.strictEqual(exitCode, 2); + }); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.mock().never(); + it(`should error out when executing on text with configType:${configType}`, async () => { + const exitCode = await cli.execute("--print-config=myFile.js", "foo = bar;", useFlatConfig); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + assert.isTrue(log.info.notCalled); + assert.isTrue(log.error.calledOnce); + assert.strictEqual(exitCode, 2); }); + }); + + // --------- + }); - const exitCode = await localCLI.execute("--fix-dry-run ."); - assert.strictEqual(exitCode, 1); + describe("when given a config file", () => { + it("should load the specified config file", async () => { + const configPath = getFixturePath(".eslintrc"); + const filePath = getFixturePath("passing.js"); + await cli.execute(`--config ${configPath} ${filePath}`); }); + }); - it("should provide fix predicate when in fix-dry-run mode and quiet mode", async () => { - const report = [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 1, - message: "Fake message" - } - ], - errorCount: 0, - warningCount: 1 - }]; + describe("eslintrc Only", () => { + + describe("Environments", () => { + + describe("when given a config with environment set to browser", () => { + it("should execute without any errors", async () => { + const configPath = getFixturePath("configurations", "env-browser.json"); + const filePath = getFixturePath("globals-browser.js"); + const code = `--config ${configPath} ${filePath}`; + + const exit = await cli.execute(code); + + assert.strictEqual(exit, 0); + }); + }); + + describe("when given a config with environment set to Node.js", () => { + it("should execute without any errors", async () => { + const configPath = getFixturePath("configurations", "env-node.json"); + const filePath = getFixturePath("globals-node.js"); + const code = `--config ${configPath} ${filePath}`; - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); + const exit = await cli.execute(code); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.getErrorResults = sinon.stub().returns([]); - fakeESLint.outputFixes = sinon.mock().never(); + assert.strictEqual(exit, 0); + }); + }); + + describe("when given a config with environment set to Nashorn", () => { + it("should execute without any errors", async () => { + const configPath = getFixturePath("configurations", "env-nashorn.json"); + const filePath = getFixturePath("globals-nashorn.js"); + const code = `--config ${configPath} ${filePath}`; - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + const exit = await cli.execute(code); + + assert.strictEqual(exit, 0); + }); }); - const exitCode = await localCLI.execute("--fix-dry-run --quiet ."); + describe("when given a config with environment set to WebExtensions", () => { + it("should execute without any errors", async () => { + const configPath = getFixturePath("configurations", "env-webextensions.json"); + const filePath = getFixturePath("globals-webextensions.js"); + const code = `--config ${configPath} ${filePath}`; - assert.strictEqual(exitCode, 0); + const exit = await cli.execute(code); + assert.strictEqual(exit, 0); + }); + }); }); - it("should allow executing on text", async () => { + describe("when loading a custom rule", () => { + it("should return an error when rule isn't found", async () => { + const rulesPath = getFixturePath("rules", "wrong"); + const configPath = getFixturePath("rules", "eslint.json"); + const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); + const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; - const report = [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ], - errorCount: 1, - warningCount: 0 - }]; + await stdAssert.rejects(async () => { + const exit = await cli.execute(code); + + assert.strictEqual(exit, 2); + }, /Error while loading rule 'custom-rule': Boom!/u); + }); + + it("should return a warning when rule is matched", async () => { + const rulesPath = getFixturePath("rules"); + const configPath = getFixturePath("rules", "eslint.json"); + const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); + const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); + await cli.execute(code); - Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); - sinon.stub(fakeESLint.prototype, "lintText").returns(report); - sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); - fakeESLint.outputFixes = sinon.mock().never(); + assert.isTrue(log.info.calledOnce); + assert.isTrue(log.info.neverCalledWith("")); + }); + + it("should return warnings from multiple rules in different directories", async () => { + const rulesPath = getFixturePath("rules", "dir1"); + const rulesPath2 = getFixturePath("rules", "dir2"); + const configPath = getFixturePath("rules", "multi-rulesdirs.json"); + const filePath = getFixturePath("rules", "test-multi-rulesdirs.js"); + const code = `--rulesdir ${rulesPath} --rulesdir ${rulesPath2} --config ${configPath} --no-ignore ${filePath}`; + const exit = await cli.execute(code); + + const call = log.info.getCall(0); - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + assert.isTrue(log.info.calledOnce); + assert.isTrue(call.args[0].includes("String!")); + assert.isTrue(call.args[0].includes("Literal!")); + assert.isTrue(call.args[0].includes("2 problems")); + assert.isTrue(log.info.neverCalledWith("")); + assert.strictEqual(exit, 1); }); - const exitCode = await localCLI.execute("--fix-dry-run .", "foo = bar;"); - assert.strictEqual(exitCode, 1); }); - it("should not call ESLint and return 2 when used with --fix", async () => { + describe("when executing with no-eslintrc flag", () => { + it("should ignore a local config file", async () => { + const filePath = getFixturePath("eslintrc", "quotes.js"); + const exit = await cli.execute(`--no-eslintrc --no-ignore ${filePath}`); - // create a fake ESLint class to test with - const fakeESLint = sinon.mock().never(); - - localCLI = proxyquire("../../lib/cli", { - "./eslint": { ESLint: fakeESLint }, - "./shared/logging": log + assert.isTrue(log.info.notCalled); + assert.strictEqual(exit, 0); }); + }); - const exitCode = await localCLI.execute("--fix --fix-dry-run .", "foo = bar;"); + describe("when executing without no-eslintrc flag", () => { + it("should load a local config file", async () => { + const filePath = getFixturePath("eslintrc", "quotes.js"); + const exit = await cli.execute(`--no-ignore ${filePath}`); - assert.strictEqual(exitCode, 2); + assert.isTrue(log.info.calledOnce); + assert.strictEqual(exit, 1); + }); }); - }); - describe("when passing --print-config", () => { - it("should print out the configuration", async () => { - const filePath = getFixturePath("xxxx"); + describe("when executing without env flag", () => { + it("should not define environment-specific globals", async () => { + const files = [ + getFixturePath("globals-browser.js"), + getFixturePath("globals-node.js") + ]; - const exitCode = await cli.execute(`--print-config ${filePath}`); + await cli.execute(`--no-eslintrc --config ./conf/eslint-recommended.js --no-ignore ${files.join(" ")}`); - assert.isTrue(log.info.calledOnce); - assert.strictEqual(exitCode, 0); + assert.strictEqual(log.info.args[0][0].split("\n").length, 10); + }); }); - it("should error if any positional file arguments are passed", async () => { - const filePath1 = getFixturePath("files", "bar.js"); - const filePath2 = getFixturePath("files", "foo.js"); - const exitCode = await cli.execute(`--print-config ${filePath1} ${filePath2}`); + describe("when supplied with a plugin", () => { + it("should pass plugins to ESLint", async () => { + const examplePluginName = "eslint-plugin-example"; + + await verifyESLintOpts(`--no-ignore --plugin ${examplePluginName} foo.js`, { + overrideConfig: { + plugins: [examplePluginName] + } + }); + }); - assert.isTrue(log.info.notCalled); - assert.isTrue(log.error.calledOnce); - assert.strictEqual(exitCode, 2); }); - it("should error out when executing on text", async () => { - const exitCode = await cli.execute("--print-config=myFile.js", "foo = bar;"); + describe("when supplied with a plugin-loading path", () => { + it("should pass the option to ESLint", async () => { + const examplePluginDirPath = "foo/bar"; - assert.isTrue(log.info.notCalled); - assert.isTrue(log.error.calledOnce); - assert.strictEqual(exitCode, 2); + await verifyESLintOpts(`--resolve-plugins-relative-to ${examplePluginDirPath} foo.js`, { + resolvePluginsRelativeTo: examplePluginDirPath + }); + }); }); + + }); + }); diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index d06ab526772..1af6c2e1d8b 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -150,7 +150,6 @@ describe("FlatESLint", () => { cacheLocation: "", cwd: "foo", errorOnUnmatchedPattern: "", - extensions: "", fix: "", fixTypes: ["xyz"], globInputPaths: "", @@ -169,7 +168,6 @@ describe("FlatESLint", () => { "- 'cacheLocation' must be a non-empty string.", "- 'cwd' must be an absolute path.", "- 'errorOnUnmatchedPattern' must be a boolean.", - "- 'extensions' must be an array of non-empty strings or null.", "- 'fix' must be a boolean or a function.", "- 'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".", "- 'globInputPaths' must be a boolean.", @@ -219,6 +217,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fixableErrorCount, 3); assert.strictEqual(results[0].fixableWarningCount, 0); assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report the total and per file warnings when using local cwd .eslintrc", async () => { @@ -244,6 +243,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fixableErrorCount, 0); assert.strictEqual(results[0].fixableWarningCount, 3); assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report one message when using specific config file", async () => { @@ -262,6 +262,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].warningCount, 0); assert.strictEqual(results[0].fatalErrorCount, 0); assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report the filename when passed in", async () => { @@ -296,6 +297,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fixableErrorCount, 0); assert.strictEqual(results[0].fixableWarningCount, 0); assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should not return a warning when given a filename by --stdin-filename in excluded files list if warnIgnored is false", async () => { @@ -349,6 +351,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); assert.strictEqual(results[0].messages[0].severity, 2); assert.strictEqual(results[0].messages[0].output, void 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return a message and fixed text when in fix mode", async () => { @@ -370,6 +373,7 @@ describe("FlatESLint", () => { { filePath: getFixturePath("passing.js"), messages: [], + suppressedMessages: [], errorCount: 0, warningCount: 0, fatalErrorCount: 0, @@ -412,6 +416,7 @@ describe("FlatESLint", () => { nodeType: "Identifier" } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 0, @@ -451,6 +456,7 @@ describe("FlatESLint", () => { column: 19 } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 1, @@ -490,6 +496,7 @@ describe("FlatESLint", () => { column: 10 } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 1, @@ -537,6 +544,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[0].source, void 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should not return a `source` property when fixes are applied", async () => { @@ -578,6 +586,7 @@ describe("FlatESLint", () => { column: 19 } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 1, @@ -601,6 +610,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, getFixturePath("node_modules/passing.js")); assert.strictEqual(results[0].messages[0].message, expectedMsg); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should warn when deprecated rules are found in a config", async () => { @@ -666,6 +676,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 1); assert.strictEqual(results[0].messages[0].message, "Parsing error: Boom!"); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report zero messages when given a config file and a valid file", async () => { @@ -678,6 +689,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should handle multiple patterns with overlapping files", async () => { @@ -690,6 +702,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report zero messages when given a config file and a valid file and espree as parser", async () => { @@ -708,6 +721,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report zero messages when given a config file and a valid file and esprima as parser", async () => { @@ -724,6 +738,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should throw an error when given a config file and a valid file and invalid parser", async () => { @@ -742,46 +757,23 @@ describe("FlatESLint", () => { it("should report zero messages when given a directory with a .js2 file", async () => { eslint = new FlatESLint({ cwd: path.join(fixtureDir, ".."), - extensions: [".js2"], - overrideConfigFile: getFixturePath("eslint.config.js") + overrideConfigFile: getFixturePath("eslint.config.js"), + overrideConfig: { + files: ["**/*.js2"] + } }); const results = await eslint.lintFiles([getFixturePath("files/foo.js2")]); assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0); - }); - - it("should fall back to defaults when extensions is set to an empty array", async () => { - eslint = new FlatESLint({ - cwd: getFixturePath(), - overrideConfigFile: true, - ignore: false, - overrideConfig: { - rules: { - quotes: ["error", "double"] - } - }, - extensions: [] - }); - - const results = await eslint.lintFiles([getFixturePath("single-quoted.js")]); - - assert.strictEqual(results.length, 1); - assert.strictEqual(results[0].messages.length, 1); - assert.strictEqual(results[0].messages[0].ruleId, "quotes"); - assert.strictEqual(results[0].messages[0].severity, 2); - assert.strictEqual(results[0].errorCount, 1); - assert.strictEqual(results[0].warningCount, 0); - assert.strictEqual(results[0].fatalErrorCount, 0); - assert.strictEqual(results[0].fixableErrorCount, 1); - assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report zero messages when given a directory with a .js and a .js2 file", async () => { eslint = new FlatESLint({ - extensions: [".js", ".js2"], ignore: false, cwd: getFixturePath(".."), + overrideConfig: { files: ["**/*.js", "**/*.js2"] }, overrideConfigFile: getFixturePath("eslint.config.js") }); const results = await eslint.lintFiles(["fixtures/files/"]); @@ -789,13 +781,14 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report zero messages when given a '**' pattern with a .js and a .js2 file", async () => { eslint = new FlatESLint({ - extensions: [".js", ".js2"], ignore: false, cwd: path.join(fixtureDir, ".."), + overrideConfig: { files: ["**/*.js", "**/*.js2"] }, overrideConfigFile: getFixturePath("eslint.config.js") }); @@ -804,13 +797,15 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); + assert.strictEqual(results[1].suppressedMessages.length, 0); }); it("should resolve globs when 'globInputPaths' option is true", async () => { eslint = new FlatESLint({ - extensions: [".js", ".js2"], ignore: false, cwd: getFixturePath(".."), + overrideConfig: { files: ["**/*.js", "**/*.js2"] }, overrideConfigFile: getFixturePath("eslint.config.js") }); @@ -819,13 +814,15 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); + assert.strictEqual(results[1].suppressedMessages.length, 0); }); it("should not resolve globs when 'globInputPaths' option is false", async () => { eslint = new FlatESLint({ - extensions: [".js", ".js2"], ignore: false, cwd: getFixturePath(".."), + overrideConfig: { files: ["**/*.js", "**/*.js2"] }, overrideConfigFile: true, globInputPaths: false }); @@ -851,6 +848,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fixableErrorCount, 0); assert.strictEqual(results[0].fixableWarningCount, 0); assert.strictEqual(results[0].messages[0].message, expectedMsg); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should report on globs with explicit inclusion of dotfiles", async () => { @@ -994,6 +992,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fatalErrorCount, 0); assert.strictEqual(results[0].fixableErrorCount, 0); assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return two messages when given a file in excluded files list while ignore is off", async () => { @@ -1017,13 +1016,14 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages[0].severity, 2); assert.strictEqual(results[0].messages[1].ruleId, "no-undef"); assert.strictEqual(results[0].messages[1].severity, 2); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); }); it("should report zero messages when given a pattern with a .js and a .js2 file", async () => { eslint = new FlatESLint({ - extensions: [".js", ".js2"], + overrideConfig: { files: ["**/*.js", "**/*.js2"] }, ignore: false, cwd: path.join(fixtureDir, ".."), overrideConfigFile: true @@ -1032,7 +1032,9 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[1].suppressedMessages.length, 0); }); it("should return one error message when given a config with rules with options and severity level set to error", async () => { @@ -1057,6 +1059,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fatalErrorCount, 0); assert.strictEqual(results[0].fixableErrorCount, 1); assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return 5 results when given a config and a directory of 5 valid files", async () => { @@ -1082,6 +1085,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].fixableErrorCount, 0); assert.strictEqual(results[0].fixableWarningCount, 0); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert.strictEqual(path.relative(formattersDir, results[1].filePath), "broken.js"); assert.strictEqual(results[1].errorCount, 0); assert.strictEqual(results[1].warningCount, 0); @@ -1089,6 +1093,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[1].fixableErrorCount, 0); assert.strictEqual(results[1].fixableWarningCount, 0); assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[1].suppressedMessages.length, 0); assert.strictEqual(path.relative(formattersDir, results[2].filePath), "cwd.js"); assert.strictEqual(results[2].errorCount, 0); assert.strictEqual(results[2].warningCount, 0); @@ -1096,6 +1101,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[2].fixableErrorCount, 0); assert.strictEqual(results[2].fixableWarningCount, 0); assert.strictEqual(results[2].messages.length, 0); + assert.strictEqual(results[2].suppressedMessages.length, 0); assert.strictEqual(path.relative(formattersDir, results[3].filePath), "simple.js"); assert.strictEqual(results[3].errorCount, 0); assert.strictEqual(results[3].warningCount, 0); @@ -1103,6 +1109,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[3].fixableErrorCount, 0); assert.strictEqual(results[3].fixableWarningCount, 0); assert.strictEqual(results[3].messages.length, 0); + assert.strictEqual(results[3].suppressedMessages.length, 0); assert.strictEqual(path.relative(formattersDir, results[4].filePath), path.join("test", "simple.js")); assert.strictEqual(results[4].errorCount, 0); assert.strictEqual(results[4].warningCount, 0); @@ -1110,6 +1117,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[4].fixableErrorCount, 0); assert.strictEqual(results[4].fixableWarningCount, 0); assert.strictEqual(results[4].messages.length, 0); + assert.strictEqual(results[4].suppressedMessages.length, 0); }); it("should return zero messages when given a config with browser globals", async () => { @@ -1121,6 +1129,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0, "Should have no messages."); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return zero messages when given an option to add browser globals", async () => { @@ -1143,6 +1152,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return zero messages when given a config with sourceType set to commonjs and Node.js globals", async () => { @@ -1154,6 +1164,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0, "Should have no messages."); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should not return results from previous call when calling more than once", async () => { @@ -1176,12 +1187,14 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].filePath, failFilePath); assert.strictEqual(results[0].messages.length, 1); assert.strictEqual(results[0].messages[0].ruleId, "semi"); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert.strictEqual(results[0].messages[0].severity, 2); results = await eslint.lintFiles([passFilePath]); assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, passFilePath); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return zero messages when executing a file with a shebang", async () => { @@ -1194,6 +1207,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0, "Should have lint messages."); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should return zero messages when executing without a config file", async () => { @@ -1208,6 +1222,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, filePath); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); // working @@ -1328,6 +1343,7 @@ describe("FlatESLint", () => { { filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/multipass.js")), messages: [], + suppressedMessages: [], errorCount: 0, warningCount: 0, fatalErrorCount: 0, @@ -1339,6 +1355,7 @@ describe("FlatESLint", () => { { filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/ok.js")), messages: [], + suppressedMessages: [], errorCount: 0, warningCount: 0, fatalErrorCount: 0, @@ -1361,6 +1378,7 @@ describe("FlatESLint", () => { severity: 2 } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 0, @@ -1384,6 +1402,7 @@ describe("FlatESLint", () => { severity: 2 } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 0, @@ -1434,6 +1453,8 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 2, "Expected two messages."); assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); it("should return two messages when executing with cli option that specifies a plugin", async () => { @@ -1449,6 +1470,8 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 2); assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); it("should return two messages when executing with cli option that specifies preloaded plugin", async () => { @@ -1467,6 +1490,8 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 2); assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule"); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); }); @@ -1547,7 +1572,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], ignore: false }); const file = getFixturePath("cache/src", "test-file.js"); @@ -1575,7 +1599,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], ignore: false }); const file = getFixturePath("cache/src", "test-file.js"); @@ -1599,7 +1622,6 @@ describe("FlatESLint", () => { "no-console": 0 } }, - extensions: ["js"], ignore: false }); const file = getFixturePath("cli-engine", "console.js"); @@ -1627,7 +1649,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], ignore: false }); @@ -1660,7 +1681,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], ignore: false }); @@ -1691,7 +1711,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], ignore: false }); @@ -1721,7 +1740,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], ignore: false }); @@ -1750,7 +1768,6 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], cwd: path.join(fixtureDir, "..") }; @@ -1791,8 +1808,7 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); @@ -1826,8 +1842,7 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); @@ -1870,8 +1885,7 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); @@ -1909,8 +1923,7 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } }); assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); @@ -1932,8 +1945,7 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } }); assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); @@ -1956,8 +1968,7 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } }); const file = getFixturePath("cli-engine", "console.js"); @@ -1980,8 +1991,8 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } + }); const file = getFixturePath("cli-engine", "console.js"); @@ -2012,7 +2023,7 @@ describe("FlatESLint", () => { "no-unused-vars": 2 } }, - extensions: ["js"], + cwd: path.join(fixtureDir, "..") }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); @@ -2051,8 +2062,8 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } + }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); @@ -2090,8 +2101,8 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } + }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); @@ -2131,8 +2142,8 @@ describe("FlatESLint", () => { "no-console": 0, "no-unused-vars": 2 } - }, - extensions: ["js"] + } + }); const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); @@ -2185,17 +2196,17 @@ describe("FlatESLint", () => { } }, { - files: ["**/*.txt/*.txt"] + files: ["**/*.txt", "**/*.txt/*.txt"] } ], - - extensions: ["js", "txt"], cwd: path.join(fixtureDir, "..") }); const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("processors", "test", "test-processor.txt"))]); assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); it("should run processors when calling lintFiles with config file that specifies preloaded processor", async () => { @@ -2225,16 +2236,17 @@ describe("FlatESLint", () => { } }, { - files: ["**/*.txt/*.txt"] + files: ["**/*.txt", "**/*.txt/*.txt"] } ], - extensions: ["js", "txt"], cwd: path.join(fixtureDir, "..") }); const results = await eslint.lintFiles([getFixturePath("processors", "test", "test-processor.txt")]); assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); it("should run processors when calling lintText with config file that specifies preloaded processor", async () => { @@ -2264,16 +2276,16 @@ describe("FlatESLint", () => { } }, { - files: ["**/*.txt/*.txt"] + files: ["**/*.txt", "**/*.txt/*.txt"] } ], - extensions: ["js", "txt"], ignore: false }); const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should run processors when calling lintText with processor resolves same extension but different content correctly", async () => { @@ -2314,9 +2326,11 @@ describe("FlatESLint", () => { "no-console": 2, "no-unused-vars": 2 } + }, + { + files: ["**/*.txt"] } ], - extensions: ["txt"], ignore: false }); const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); @@ -2324,6 +2338,8 @@ describe("FlatESLint", () => { assert.strictEqual(count, 2); assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); describe("autofixing with processors", () => { @@ -2349,23 +2365,28 @@ describe("FlatESLint", () => { it("should run in autofix mode when using a processor that supports autofixing", async () => { eslint = new FlatESLint({ overrideConfigFile: true, - overrideConfig: { - files: ["**/*.html"], - plugins: { - test: { processors: { html: Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) } } + overrideConfig: [ + { + files: ["**/*.html"], + plugins: { + test: { processors: { html: Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) } } + }, + processor: "test/html", + rules: { + semi: 2 + } }, - processor: "test/html", - rules: { - semi: 2 + { + files: ["**/*.txt"] } - }, - extensions: ["js", "txt"], + ], ignore: false, fix: true }); const results = await eslint.lintText("", { filePath: "foo.html" }); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert.strictEqual(results[0].output, ""); }); @@ -2388,28 +2409,34 @@ describe("FlatESLint", () => { const results = await eslint.lintText("", { filePath: "foo.html" }); assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert(!Object.prototype.hasOwnProperty.call(results[0], "output")); }); it("should not run in autofix mode when `fix: true` is not provided, even if the processor supports autofixing", async () => { eslint = new FlatESLint({ overrideConfigFile: true, - overrideConfig: { - files: ["**/*.html"], - plugins: { - test: { processors: { html: Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) } } + overrideConfig: [ + { + files: ["**/*.html"], + plugins: { + test: { processors: { html: Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) } } + }, + processor: "test/html", + rules: { + semi: 2 + } }, - processor: "test/html", - rules: { - semi: 2 + { + files: ["**/*.txt"] } - }, - extensions: ["js", "txt"], + ], ignore: false }); const results = await eslint.lintText("", { filePath: "foo.html" }); assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert(!Object.prototype.hasOwnProperty.call(results[0], "output")); }); }); @@ -2501,7 +2528,7 @@ describe("FlatESLint", () => { beforeEach(() => (id = Date.now().toString())); afterEach(async () => fsp.rmdir(root, { recursive: true, force: true })); - it("should lint only JavaScript blocks if '--ext' was not given.", async () => { + it("should lint only JavaScript blocks.", async () => { const teardown = createCustomTeardown({ cwd: path.join(root, id), files: { @@ -2533,51 +2560,8 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages.length, 1, "Should have one message."); assert.strictEqual(results[0].messages[0].ruleId, "semi"); assert.strictEqual(results[0].messages[0].line, 2, "Message should be on line 2."); - }); + assert.strictEqual(results[0].suppressedMessages.length, 0); - it("should fix only JavaScript blocks if '--ext' was not given.", async () => { - const teardown = createCustomTeardown({ - cwd: path.join(root, id), - files: { - ...commonFiles, - "eslint.config.js": `module.exports = [ - { - plugins: { - markdown: require("eslint-plugin-markdown") - } - }, - { - files: ["**/*.js"], - rules: { semi: "error" } - }, - { - files: ["**/*.md"], - processor: "markdown/markdown" - } - ];` - } - }); - - await teardown.prepare(); - eslint = new FlatESLint({ cwd: teardown.getPath(), fix: true }); - const results = await eslint.lintFiles(["test.md"]); - - assert.strictEqual(results.length, 1); - assert.strictEqual(results[0].messages.length, 0); - assert.strictEqual(results[0].output, unIndent` - \`\`\`js - console.log("hello");${/* ← fixed */""} - \`\`\` - \`\`\`html -
Hello
- - - \`\`\` - `); }); it("should lint HTML blocks as well with multiple processors if represented in config.", async () => { @@ -2609,7 +2593,7 @@ describe("FlatESLint", () => { }); await teardown.prepare(); - eslint = new FlatESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); + eslint = new FlatESLint({ cwd: teardown.getPath(), overrideConfig: { files: ["**/*.html"] } }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1, "Should have one result."); @@ -2618,6 +2602,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages[0].line, 2, "First error should be on line 2"); assert.strictEqual(results[0].messages[1].ruleId, "semi"); // JS block in HTML block assert.strictEqual(results[0].messages[1].line, 7, "Second error should be on line 7."); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should fix HTML blocks as well with multiple processors if represented in config.", async () => { @@ -2649,11 +2634,12 @@ describe("FlatESLint", () => { }); await teardown.prepare(); - eslint = new FlatESLint({ cwd: teardown.getPath(), extensions: ["js", "html"], fix: true }); + eslint = new FlatESLint({ cwd: teardown.getPath(), overrideConfig: { files: ["**/*.html"] }, fix: true }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); assert.strictEqual(results[0].output, unIndent` \`\`\`js console.log("hello");${/* ← fixed */""} @@ -2708,7 +2694,7 @@ describe("FlatESLint", () => { }); await teardown.prepare(); - eslint = new FlatESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); + eslint = new FlatESLint({ cwd: teardown.getPath(), overrideConfig: { files: ["**/*.html"] } }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -2717,6 +2703,8 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages[0].line, 2); assert.strictEqual(results[0].messages[1].ruleId, "no-console"); assert.strictEqual(results[0].messages[1].line, 7); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); 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 () => { @@ -2757,7 +2745,7 @@ describe("FlatESLint", () => { }); await teardown.prepare(); - eslint = new FlatESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); + eslint = new FlatESLint({ cwd: teardown.getPath(), overrideConfig: { files: ["**/*.html"] } }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -2768,6 +2756,7 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages[1].line, 7); assert.strictEqual(results[0].messages[2].ruleId, "no-console"); assert.strictEqual(results[0].messages[2].line, 10); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should throw an error if invalid processor was specified.", async () => { @@ -2920,6 +2909,7 @@ describe("FlatESLint", () => { assert.strictEqual(messages.length, 1); assert.strictEqual(messages[0].severity, 1); assert.strictEqual(messages[0].message, "Unused eslint-disable directive (no problems were reported from 'eqeqeq')."); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); describe("the runtime option overrides config files.", () => { @@ -2969,6 +2959,7 @@ describe("FlatESLint", () => { assert.strictEqual(messages.length, 1); assert.strictEqual(messages[0].severity, 2); assert.strictEqual(messages[0].message, "Unused eslint-disable directive (no problems were reported from 'eqeqeq')."); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); }); }); @@ -3704,6 +3695,7 @@ describe("FlatESLint", () => { nodeType: "CallExpression" } ], + suppressedMessages: [], errorCount: 2, warningCount: 0, fatalErrorCount: 0, @@ -3884,6 +3876,7 @@ describe("FlatESLint", () => { assert.strictEqual(messages.length, 1); assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(results[0].suppressedMessages.length, 0); }); it("should not report a violation by default", async () => { @@ -3909,6 +3902,8 @@ describe("FlatESLint", () => { const messages = results[0].messages; assert.strictEqual(messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 1); + assert.strictEqual(results[0].suppressedMessages[0].ruleId, "no-alert"); }); }); @@ -3935,6 +3930,7 @@ describe("FlatESLint", () => { nodeType: null } ], + suppressedMessages: [], errorCount: 1, warningCount: 0, fatalErrorCount: 0, @@ -4483,6 +4479,7 @@ describe("FlatESLint", () => { // Expected to be an 'eqeqeq' error because the file matches to `$CWD/foo/*.js`. assert.deepStrictEqual(results, [ { + suppressedMessages: [], errorCount: 1, filePath: path.join(getPath(), "foo/test.js"), fixableErrorCount: 0, @@ -4519,6 +4516,7 @@ describe("FlatESLint", () => { // Expected to be no errors because the file doesn't match to `$CWD/foo/*.js`. assert.deepStrictEqual(results, [ { + suppressedMessages: [], errorCount: 0, filePath: path.join(getPath(), "node_modules/myconf/foo/test.js"), fixableErrorCount: 0, @@ -4570,6 +4568,7 @@ describe("FlatESLint", () => { // Expected to be no errors because the file matches to `$CWD/foo/*.js`. assert.deepStrictEqual(results, [ { + suppressedMessages: [], errorCount: 0, filePath: path.join(getPath(), "foo/test.js"), fixableErrorCount: 0, @@ -4593,6 +4592,7 @@ describe("FlatESLint", () => { // Expected to be an 'eqeqeq' error because the file doesn't match to `$CWD/foo/*.js`. assert.deepStrictEqual(results, [ { + suppressedMessages: [], errorCount: 1, filePath: path.join(getPath(), "bar/myconf/foo/test.js"), fixableErrorCount: 0, diff --git a/tests/lib/options.js b/tests/lib/options.js index c84e46d9afd..4d3507a4764 100644 --- a/tests/lib/options.js +++ b/tests/lib/options.js @@ -10,7 +10,14 @@ //------------------------------------------------------------------------------ const assert = require("chai").assert, - options = require("../../lib/options"); + createOptions = require("../../lib/options"); + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- + +const eslintrcOptions = createOptions(false); +const flatOptions = createOptions(true); //------------------------------------------------------------------------------ // Tests @@ -21,243 +28,360 @@ const assert = require("chai").assert, */ describe("options", () => { - describe("--help", () => { - it("should return true for .help when passed", () => { - const currentOptions = options.parse("--help"); - assert.isTrue(currentOptions.help); - }); - }); + describe("Common options", () => { - describe("-h", () => { - it("should return true for .help when passed", () => { - const currentOptions = options.parse("-h"); + [eslintrcOptions, flatOptions].forEach(options => { - assert.isTrue(currentOptions.help); - }); - }); + describe("--help", () => { + it("should return true for .help when passed", () => { + const currentOptions = options.parse("--help"); - describe("--config", () => { - it("should return a string for .config when passed a string", () => { - const currentOptions = options.parse("--config file"); + assert.isTrue(currentOptions.help); + }); + }); - assert.isString(currentOptions.config); - assert.strictEqual(currentOptions.config, "file"); - }); - }); + describe("-h", () => { + it("should return true for .help when passed", () => { + const currentOptions = options.parse("-h"); - describe("-c", () => { - it("should return a string for .config when passed a string", () => { - const currentOptions = options.parse("-c file"); + assert.isTrue(currentOptions.help); + }); + }); - assert.isString(currentOptions.config); - assert.strictEqual(currentOptions.config, "file"); - }); - }); + describe("--config", () => { + it("should return a string for .config when passed a string", () => { + const currentOptions = options.parse("--config file"); - describe("--ext", () => { - it("should return an array with one item when passed .jsx", () => { - const currentOptions = options.parse("--ext .jsx"); + assert.isString(currentOptions.config); + assert.strictEqual(currentOptions.config, "file"); + }); + }); - assert.isArray(currentOptions.ext); - assert.strictEqual(currentOptions.ext[0], ".jsx"); - }); + describe("-c", () => { + it("should return a string for .config when passed a string", () => { + const currentOptions = options.parse("-c file"); - it("should return an array with two items when passed .js and .jsx", () => { - const currentOptions = options.parse("--ext .jsx --ext .js"); + assert.isString(currentOptions.config); + assert.strictEqual(currentOptions.config, "file"); + }); + }); - assert.isArray(currentOptions.ext); - assert.strictEqual(currentOptions.ext[0], ".jsx"); - assert.strictEqual(currentOptions.ext[1], ".js"); - }); + describe("--format", () => { + it("should return a string for .format when passed a string", () => { + const currentOptions = options.parse("--format compact"); - it("should return an array with two items when passed .jsx,.js", () => { - const currentOptions = options.parse("--ext .jsx,.js"); + assert.isString(currentOptions.format); + assert.strictEqual(currentOptions.format, "compact"); + }); - assert.isArray(currentOptions.ext); - assert.strictEqual(currentOptions.ext[0], ".jsx"); - assert.strictEqual(currentOptions.ext[1], ".js"); - }); + it("should return stylish for .format when not passed", () => { + const currentOptions = options.parse(""); - it("should not exist when not passed", () => { - const currentOptions = options.parse(""); + assert.isString(currentOptions.format); + assert.strictEqual(currentOptions.format, "stylish"); + }); + }); - assert.notProperty(currentOptions, "ext"); - }); - }); + describe("-f", () => { + it("should return a string for .format when passed a string", () => { + const currentOptions = options.parse("-f compact"); - describe("--rulesdir", () => { - it("should return a string for .rulesdir when passed a string", () => { - const currentOptions = options.parse("--rulesdir /morerules"); + assert.isString(currentOptions.format); + assert.strictEqual(currentOptions.format, "compact"); + }); + }); - assert.isArray(currentOptions.rulesdir); - assert.deepStrictEqual(currentOptions.rulesdir, ["/morerules"]); - }); - }); + describe("--version", () => { + it("should return true for .version when passed", () => { + const currentOptions = options.parse("--version"); - describe("--format", () => { - it("should return a string for .format when passed a string", () => { - const currentOptions = options.parse("--format compact"); + assert.isTrue(currentOptions.version); + }); + }); - assert.isString(currentOptions.format); - assert.strictEqual(currentOptions.format, "compact"); - }); + describe("-v", () => { + it("should return true for .version when passed", () => { + const currentOptions = options.parse("-v"); - it("should return stylish for .format when not passed", () => { - const currentOptions = options.parse(""); + assert.isTrue(currentOptions.version); + }); + }); - assert.isString(currentOptions.format); - assert.strictEqual(currentOptions.format, "stylish"); - }); - }); + describe("when asking for help", () => { + it("should return string of help text when called", () => { + const helpText = options.generateHelp(); - describe("-f", () => { - it("should return a string for .format when passed a string", () => { - const currentOptions = options.parse("-f compact"); + assert.isString(helpText); + }); + }); + + describe("--no-ignore", () => { + it("should return false for .ignore when passed", () => { + const currentOptions = options.parse("--no-ignore"); - assert.isString(currentOptions.format); - assert.strictEqual(currentOptions.format, "compact"); - }); - }); + assert.isFalse(currentOptions.ignore); + }); + }); + + describe("--ignore-path", () => { + it("should return a string for .ignorePath when passed", () => { + const currentOptions = options.parse("--ignore-path .gitignore"); - describe("--version", () => { - it("should return true for .version when passed", () => { - const currentOptions = options.parse("--version"); + assert.strictEqual(currentOptions.ignorePath, ".gitignore"); + }); + }); + + describe("--ignore-pattern", () => { + it("should return a string array for .ignorePattern when passed", () => { + const currentOptions = options.parse("--ignore-pattern *.js"); + + assert.ok(currentOptions.ignorePattern); + assert.strictEqual(currentOptions.ignorePattern.length, 1); + assert.strictEqual(currentOptions.ignorePattern[0], "*.js"); + }); + + it("should return a string array for multiple values", () => { + const currentOptions = options.parse("--ignore-pattern *.js --ignore-pattern *.ts"); + + assert.ok(currentOptions.ignorePattern); + assert.strictEqual(currentOptions.ignorePattern.length, 2); + assert.strictEqual(currentOptions.ignorePattern[0], "*.js"); + assert.strictEqual(currentOptions.ignorePattern[1], "*.ts"); + }); + + it("should return a string array of properly parsed values, when those values include commas", () => { + const currentOptions = options.parse("--ignore-pattern *.js --ignore-pattern foo-{bar,baz}.js"); + + assert.ok(currentOptions.ignorePattern); + assert.strictEqual(currentOptions.ignorePattern.length, 2); + assert.strictEqual(currentOptions.ignorePattern[0], "*.js"); + assert.strictEqual(currentOptions.ignorePattern[1], "foo-{bar,baz}.js"); + }); + }); + + describe("--color", () => { + it("should return true for .color when passed --color", () => { + const currentOptions = options.parse("--color"); + + assert.isTrue(currentOptions.color); + }); + + it("should return false for .color when passed --no-color", () => { + const currentOptions = options.parse("--no-color"); + + assert.isFalse(currentOptions.color); + }); + }); + + describe("--stdin", () => { + it("should return true for .stdin when passed", () => { + const currentOptions = options.parse("--stdin"); + + assert.isTrue(currentOptions.stdin); + }); + }); + + describe("--stdin-filename", () => { + it("should return a string for .stdinFilename when passed", () => { + const currentOptions = options.parse("--stdin-filename test.js"); + + assert.strictEqual(currentOptions.stdinFilename, "test.js"); + }); + }); + + describe("--global", () => { + it("should return an array for a single occurrence", () => { + const currentOptions = options.parse("--global foo"); + + assert.isArray(currentOptions.global); + assert.strictEqual(currentOptions.global.length, 1); + assert.strictEqual(currentOptions.global[0], "foo"); + }); + + it("should split variable names using commas", () => { + const currentOptions = options.parse("--global foo,bar"); + + assert.isArray(currentOptions.global); + assert.strictEqual(currentOptions.global.length, 2); + assert.strictEqual(currentOptions.global[0], "foo"); + assert.strictEqual(currentOptions.global[1], "bar"); + }); + + it("should not split on colons", () => { + const currentOptions = options.parse("--global foo:false,bar:true"); + + assert.isArray(currentOptions.global); + assert.strictEqual(currentOptions.global.length, 2); + assert.strictEqual(currentOptions.global[0], "foo:false"); + assert.strictEqual(currentOptions.global[1], "bar:true"); + }); + + it("should concatenate successive occurrences", () => { + const currentOptions = options.parse("--global foo:true --global bar:false"); - assert.isTrue(currentOptions.version); - }); - }); + assert.isArray(currentOptions.global); + assert.strictEqual(currentOptions.global.length, 2); + assert.strictEqual(currentOptions.global[0], "foo:true"); + assert.strictEqual(currentOptions.global[1], "bar:false"); + }); + }); - describe("-v", () => { - it("should return true for .version when passed", () => { - const currentOptions = options.parse("-v"); - assert.isTrue(currentOptions.version); - }); - }); + describe("--quiet", () => { + it("should return true for .quiet when passed", () => { + const currentOptions = options.parse("--quiet"); - describe("when asking for help", () => { - it("should return string of help text when called", () => { - const helpText = options.generateHelp(); + assert.isTrue(currentOptions.quiet); + }); + }); - assert.isString(helpText); - }); - }); + describe("--max-warnings", () => { + it("should return correct value for .maxWarnings when passed", () => { + const currentOptions = options.parse("--max-warnings 10"); - describe("--no-ignore", () => { - it("should return false for .ignore when passed", () => { - const currentOptions = options.parse("--no-ignore"); + assert.strictEqual(currentOptions.maxWarnings, 10); + }); - assert.isFalse(currentOptions.ignore); - }); - }); + it("should return -1 for .maxWarnings when not passed", () => { + const currentOptions = options.parse(""); - describe("--ignore-path", () => { - it("should return a string for .ignorePath when passed", () => { - const currentOptions = options.parse("--ignore-path .gitignore"); + assert.strictEqual(currentOptions.maxWarnings, -1); + }); - assert.strictEqual(currentOptions.ignorePath, ".gitignore"); - }); - }); + it("should throw an error when supplied with a non-integer", () => { + assert.throws(() => { + options.parse("--max-warnings 10.2"); + }, /Invalid value for option 'max-warnings' - expected type Int/u); + }); + }); - describe("--ignore-pattern", () => { - it("should return a string array for .ignorePattern when passed", () => { - const currentOptions = options.parse("--ignore-pattern *.js"); + describe("--init", () => { + it("should return true for --init when passed", () => { + const currentOptions = options.parse("--init"); - assert.ok(currentOptions.ignorePattern); - assert.strictEqual(currentOptions.ignorePattern.length, 1); - assert.strictEqual(currentOptions.ignorePattern[0], "*.js"); - }); + assert.isTrue(currentOptions.init); + }); + }); - it("should return a string array for multiple values", () => { - const currentOptions = options.parse("--ignore-pattern *.js --ignore-pattern *.ts"); + describe("--fix", () => { + it("should return true for --fix when passed", () => { + const currentOptions = options.parse("--fix"); - assert.ok(currentOptions.ignorePattern); - assert.strictEqual(currentOptions.ignorePattern.length, 2); - assert.strictEqual(currentOptions.ignorePattern[0], "*.js"); - assert.strictEqual(currentOptions.ignorePattern[1], "*.ts"); - }); + assert.isTrue(currentOptions.fix); + }); + }); - it("should return a string array of properly parsed values, when those values include commas", () => { - const currentOptions = options.parse("--ignore-pattern *.js --ignore-pattern foo-{bar,baz}.js"); + describe("--fix-type", () => { + it("should return one value with --fix-type is passed", () => { + const currentOptions = options.parse("--fix-type problem"); - assert.ok(currentOptions.ignorePattern); - assert.strictEqual(currentOptions.ignorePattern.length, 2); - assert.strictEqual(currentOptions.ignorePattern[0], "*.js"); - assert.strictEqual(currentOptions.ignorePattern[1], "foo-{bar,baz}.js"); - }); - }); + assert.strictEqual(currentOptions.fixType.length, 1); + assert.strictEqual(currentOptions.fixType[0], "problem"); + }); - describe("--color", () => { - it("should return true for .color when passed --color", () => { - const currentOptions = options.parse("--color"); + it("should return two values when --fix-type is passed twice", () => { + const currentOptions = options.parse("--fix-type problem --fix-type suggestion"); - assert.isTrue(currentOptions.color); - }); + assert.strictEqual(currentOptions.fixType.length, 2); + assert.strictEqual(currentOptions.fixType[0], "problem"); + assert.strictEqual(currentOptions.fixType[1], "suggestion"); + }); + + it("should return two values when --fix-type is passed a comma-separated value", () => { + const currentOptions = options.parse("--fix-type problem,suggestion"); + + assert.strictEqual(currentOptions.fixType.length, 2); + assert.strictEqual(currentOptions.fixType[0], "problem"); + assert.strictEqual(currentOptions.fixType[1], "suggestion"); + }); + }); + + describe("--debug", () => { + it("should return true for --debug when passed", () => { + const currentOptions = options.parse("--debug"); + + assert.isTrue(currentOptions.debug); + }); + }); + + describe("--inline-config", () => { + it("should return false when passed --no-inline-config", () => { + const currentOptions = options.parse("--no-inline-config"); + + assert.isFalse(currentOptions.inlineConfig); + }); + + it("should return true for --inline-config when empty", () => { + const currentOptions = options.parse(""); - it("should return false for .color when passed --no-color", () => { - const currentOptions = options.parse("--no-color"); + assert.isTrue(currentOptions.inlineConfig); + }); + }); - assert.isFalse(currentOptions.color); + describe("--print-config", () => { + it("should return file path when passed --print-config", () => { + const currentOptions = options.parse("--print-config file.js"); + + assert.strictEqual(currentOptions.printConfig, "file.js"); + }); + }); }); + }); - describe("--stdin", () => { - it("should return true for .stdin when passed", () => { - const currentOptions = options.parse("--stdin"); - assert.isTrue(currentOptions.stdin); + describe("--ext", () => { + it("should return an array with one item when passed .jsx", () => { + const currentOptions = eslintrcOptions.parse("--ext .jsx"); + + assert.isArray(currentOptions.ext); + assert.strictEqual(currentOptions.ext[0], ".jsx"); }); - }); - describe("--stdin-filename", () => { - it("should return a string for .stdinFilename when passed", () => { - const currentOptions = options.parse("--stdin-filename test.js"); + it("should return an array with two items when passed .js and .jsx", () => { + const currentOptions = eslintrcOptions.parse("--ext .jsx --ext .js"); - assert.strictEqual(currentOptions.stdinFilename, "test.js"); + assert.isArray(currentOptions.ext); + assert.strictEqual(currentOptions.ext[0], ".jsx"); + assert.strictEqual(currentOptions.ext[1], ".js"); }); - }); - describe("--global", () => { - it("should return an array for a single occurrence", () => { - const currentOptions = options.parse("--global foo"); + it("should return an array with two items when passed .jsx,.js", () => { + const currentOptions = eslintrcOptions.parse("--ext .jsx,.js"); - assert.isArray(currentOptions.global); - assert.strictEqual(currentOptions.global.length, 1); - assert.strictEqual(currentOptions.global[0], "foo"); + assert.isArray(currentOptions.ext); + assert.strictEqual(currentOptions.ext[0], ".jsx"); + assert.strictEqual(currentOptions.ext[1], ".js"); }); - it("should split variable names using commas", () => { - const currentOptions = options.parse("--global foo,bar"); + it("should not exist when not passed", () => { + const currentOptions = eslintrcOptions.parse(""); - assert.isArray(currentOptions.global); - assert.strictEqual(currentOptions.global.length, 2); - assert.strictEqual(currentOptions.global[0], "foo"); - assert.strictEqual(currentOptions.global[1], "bar"); + assert.notProperty(currentOptions, "ext"); }); + }); - it("should not split on colons", () => { - const currentOptions = options.parse("--global foo:false,bar:true"); + describe("--rulesdir", () => { + it("should return a string for .rulesdir when passed a string", () => { + const currentOptions = eslintrcOptions.parse("--rulesdir /morerules"); - assert.isArray(currentOptions.global); - assert.strictEqual(currentOptions.global.length, 2); - assert.strictEqual(currentOptions.global[0], "foo:false"); - assert.strictEqual(currentOptions.global[1], "bar:true"); + assert.isArray(currentOptions.rulesdir); + assert.deepStrictEqual(currentOptions.rulesdir, ["/morerules"]); }); + }); - it("should concatenate successive occurrences", () => { - const currentOptions = options.parse("--global foo:true --global bar:false"); + describe("--parser", () => { + it("should return a string for --parser when passed", () => { + const currentOptions = eslintrcOptions.parse("--parser test"); - assert.isArray(currentOptions.global); - assert.strictEqual(currentOptions.global.length, 2); - assert.strictEqual(currentOptions.global[0], "foo:true"); - assert.strictEqual(currentOptions.global[1], "bar:false"); + assert.strictEqual(currentOptions.parser, "test"); }); }); describe("--plugin", () => { it("should return an array when passed a single occurrence", () => { - const currentOptions = options.parse("--plugin single"); + const currentOptions = eslintrcOptions.parse("--plugin single"); assert.isArray(currentOptions.plugin); assert.strictEqual(currentOptions.plugin.length, 1); @@ -265,7 +389,7 @@ describe("options", () => { }); it("should return an array when passed a comma-delimited string", () => { - const currentOptions = options.parse("--plugin foo,bar"); + const currentOptions = eslintrcOptions.parse("--plugin foo,bar"); assert.isArray(currentOptions.plugin); assert.strictEqual(currentOptions.plugin.length, 2); @@ -274,7 +398,7 @@ describe("options", () => { }); it("should return an array when passed multiple times", () => { - const currentOptions = options.parse("--plugin foo --plugin bar"); + const currentOptions = eslintrcOptions.parse("--plugin foo --plugin bar"); assert.isArray(currentOptions.plugin); assert.strictEqual(currentOptions.plugin.length, 2); @@ -283,110 +407,12 @@ describe("options", () => { }); }); - describe("--quiet", () => { - it("should return true for .quiet when passed", () => { - const currentOptions = options.parse("--quiet"); - - assert.isTrue(currentOptions.quiet); - }); - }); - - describe("--max-warnings", () => { - it("should return correct value for .maxWarnings when passed", () => { - const currentOptions = options.parse("--max-warnings 10"); - - assert.strictEqual(currentOptions.maxWarnings, 10); - }); - - it("should return -1 for .maxWarnings when not passed", () => { - const currentOptions = options.parse(""); - - assert.strictEqual(currentOptions.maxWarnings, -1); - }); - - it("should throw an error when supplied with a non-integer", () => { - assert.throws(() => { - options.parse("--max-warnings 10.2"); - }, /Invalid value for option 'max-warnings' - expected type Int/u); - }); - }); - - describe("--init", () => { - it("should return true for --init when passed", () => { - const currentOptions = options.parse("--init"); - - assert.isTrue(currentOptions.init); - }); - }); - - describe("--fix", () => { - it("should return true for --fix when passed", () => { - const currentOptions = options.parse("--fix"); - - assert.isTrue(currentOptions.fix); - }); - }); - - describe("--fix-type", () => { - it("should return one value with --fix-type is passed", () => { - const currentOptions = options.parse("--fix-type problem"); - - assert.strictEqual(currentOptions.fixType.length, 1); - assert.strictEqual(currentOptions.fixType[0], "problem"); - }); - - it("should return two values when --fix-type is passed twice", () => { - const currentOptions = options.parse("--fix-type problem --fix-type suggestion"); - - assert.strictEqual(currentOptions.fixType.length, 2); - assert.strictEqual(currentOptions.fixType[0], "problem"); - assert.strictEqual(currentOptions.fixType[1], "suggestion"); - }); - - it("should return two values when --fix-type is passed a comma-separated value", () => { - const currentOptions = options.parse("--fix-type problem,suggestion"); - - assert.strictEqual(currentOptions.fixType.length, 2); - assert.strictEqual(currentOptions.fixType[0], "problem"); - assert.strictEqual(currentOptions.fixType[1], "suggestion"); - }); - }); - - describe("--debug", () => { - it("should return true for --debug when passed", () => { - const currentOptions = options.parse("--debug"); - - assert.isTrue(currentOptions.debug); - }); - }); - - describe("--inline-config", () => { - it("should return false when passed --no-inline-config", () => { - const currentOptions = options.parse("--no-inline-config"); - - assert.isFalse(currentOptions.inlineConfig); - }); - - it("should return true for --inline-config when empty", () => { - const currentOptions = options.parse(""); - - assert.isTrue(currentOptions.inlineConfig); - }); - }); - - describe("--parser", () => { - it("should return a string for --parser when passed", () => { - const currentOptions = options.parse("--parser test"); + describe("--no-config-lookup", () => { + it("should return a string for .rulesdir when passed a string", () => { + const currentOptions = flatOptions.parse("--no-config-lookup foo.js"); - assert.strictEqual(currentOptions.parser, "test"); + assert.isFalse(currentOptions.configLookup); }); }); - describe("--print-config", () => { - it("should return file path when passed --print-config", () => { - const currentOptions = options.parse("--print-config file.js"); - - assert.strictEqual(currentOptions.printConfig, "file.js"); - }); - }); }); diff --git a/tools/internal-testers/event-generator-tester.js b/tools/internal-testers/event-generator-tester.js index ce4449a6b6a..642d8a7e0d2 100644 --- a/tools/internal-testers/event-generator-tester.js +++ b/tools/internal-testers/event-generator-tester.js @@ -4,7 +4,7 @@ */ "use strict"; -/* eslint-env mocha -- Mocha */ +/* globals describe, it -- Mocha globals */ //------------------------------------------------------------------------------ // Requirements