const path = require("path");
const defaultOptions = require("../../conf/default-cli-options");
const pkg = require("../../package.json");
-const ConfigOps = require("../shared/config-ops");
-const naming = require("../shared/naming");
-const ModuleResolver = require("../shared/relative-module-resolver");
+
+
+const {
+ Legacy: {
+ ConfigOps,
+ naming,
+ CascadingConfigArrayFactory,
+ IgnorePattern,
+ getUsedExtractedConfigs
+ }
+} = require("@eslint/eslintrc");
+
+/*
+ * For some reason, ModuleResolver must be included via filepath instead of by
+ * API exports in order to work properly. That's why this is separated out onto
+ * its own require() statement.
+ */
+const ModuleResolver = require("@eslint/eslintrc/lib/shared/relative-module-resolver");
+const { FileEnumerator } = require("./file-enumerator");
+
const { Linter } = require("../linter");
const builtInRules = require("../rules");
-const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory");
-const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array");
-const { FileEnumerator } = require("./file-enumerator");
+const loadRules = require("./load-rules");
const hash = require("./hash");
const LintResultCache = require("./lint-result-cache");
// For VSCode IntelliSense
/** @typedef {import("../shared/types").ConfigData} ConfigData */
+/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
/** @typedef {import("../shared/types").LintMessage} LintMessage */
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */
/** @typedef {import("../shared/types").Plugin} Plugin */
/**
* The options to configure a CLI engine with.
* @typedef {Object} CLIEngineOptions
- * @property {boolean} allowInlineConfig Enable or disable inline configuration comments.
- * @property {ConfigData} baseConfig Base config object, extended by all configs used with this CLIEngine instance
- * @property {boolean} cache Enable result caching.
- * @property {string} cacheLocation The cache file to use instead of .eslintcache.
- * @property {string} configFile The configuration file to use.
- * @property {string} cwd The value to use for the current working directory.
- * @property {string[]} envs An array of environments to load.
- * @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 {string[]} globals An array of global variables to declare.
- * @property {boolean} ignore False disables use of .eslintignore.
- * @property {string} ignorePath The ignore file to use instead of .eslintignore.
- * @property {string|string[]} ignorePattern One or more glob patterns to ignore.
- * @property {boolean} useEslintrc False disables looking for .eslintrc
- * @property {string} parser The name of the parser to use.
- * @property {ParserOptions} parserOptions An object of parserOption settings to use.
- * @property {string[]} plugins An array of plugins to load.
- * @property {Record<string,RuleConf>} rules An object of rules to use.
- * @property {string[]} rulePaths An array of directories to load custom rules from.
- * @property {boolean} reportUnusedDisableDirectives `true` adds reports for unused eslint-disable directives
- * @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.
- * @property {string} resolvePluginsRelativeTo The folder where plugins should be resolved from, defaulting to the CWD
+ * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
+ * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this CLIEngine instance
+ * @property {boolean} [cache] Enable result caching.
+ * @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
+ * @property {string} [configFile] The configuration file to use.
+ * @property {string} [cwd] The value to use for the current working directory.
+ * @property {string[]} [envs] An array of environments to load.
+ * @property {string[]|null} [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 {string[]} [globals] An array of global variables to declare.
+ * @property {boolean} [ignore] False disables use of .eslintignore.
+ * @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
+ * @property {string|string[]} [ignorePattern] One or more glob patterns to ignore.
+ * @property {boolean} [useEslintrc] False disables looking for .eslintrc
+ * @property {string} [parser] The name of the parser to use.
+ * @property {ParserOptions} [parserOptions] An object of parserOption settings to use.
+ * @property {string[]} [plugins] An array of plugins to load.
+ * @property {Record<string,RuleConf>} [rules] An object of rules to use.
+ * @property {string[]} [rulePaths] An array of directories to load custom rules from.
+ * @property {boolean} [reportUnusedDisableDirectives] `true` adds reports for unused eslint-disable directives
+ * @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.
+ * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD
*/
/**
* @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
*/
-/**
- * Information of deprecated rules.
- * @typedef {Object} DeprecatedRuleInfo
- * @property {string} ruleId The rule ID.
- * @property {string[]} replacedBy The rule IDs that replace this deprecated rule.
- */
-
/**
* Linting results.
* @typedef {Object} LintReport
* @param {boolean} config.fix If `true` then it does fix.
* @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
* @param {boolean} config.reportUnusedDisableDirectives If `true` then it reports unused `eslint-disable` comments.
- * @param {RegExp} config.extensionRegExp The `RegExp` object that tests if a file path has the allowed file extensions.
+ * @param {FileEnumerator} config.fileEnumerator The file enumerator to check if a path is a target or not.
* @param {Linter} config.linter The linter instance to verify.
* @returns {LintResult} The result of linting.
* @private
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
- extensionRegExp,
+ fileEnumerator,
linter
}) {
const filePath = providedFilePath || "<text>";
/**
* Check if the linter should adopt a given code block or not.
- * Currently, the linter adopts code blocks if the name matches `--ext` option.
- * In the future, `overrides` in the configuration would affect the adoption (https://github.com/eslint/rfcs/pull/20).
* @param {string} blockFilename The virtual filename of a code block.
* @returns {boolean} `true` if the linter should adopt the code block.
*/
filterCodeBlock(blockFilename) {
- return extensionRegExp.test(blockFilename);
+ return fileEnumerator.isTargetPath(blockFilename);
}
}
);
*/
function createIgnoreResult(filePath, baseDir) {
let message;
- const isHidden = /^\./u.test(path.basename(filePath));
+ const isHidden = filePath.split(path.sep)
+ .find(segment => /^\./u.test(segment));
const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
- const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
if (isHidden) {
message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
} else if (isInNodeModules) {
message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
- } else if (isInBowerComponents) {
- message = "File ignored by default. Use \"--ignore-pattern '!bower_components/*'\" to override.";
} else {
message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
}
try {
fileStats = fs.lstatSync(resolvedCacheFile);
- } catch (ex) {
+ } catch {
fileStats = null;
}
try {
return fs.statSync(resolvedPath).isDirectory();
} catch (error) {
- if (error && error.code === "ENOENT") {
+ if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
return false;
}
throw error;
resolvePluginsRelativeTo: options.resolvePluginsRelativeTo,
rulePaths: options.rulePaths,
specificConfigPath: options.configFile,
- useEslintrc: options.useEslintrc
+ useEslintrc: options.useEslintrc,
+ builtInRules,
+ loadRules,
+ eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
+ eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
});
const fileEnumerator = new FileEnumerator({
configArrayFactory,
ignore: options.ignore
});
const lintResultCache =
- options.cache ? new LintResultCache(cacheFilePath) : null;
+ options.cache ? new LintResultCache(cacheFilePath, options.cacheStrategy) : null;
const linter = new Linter({ cwd: options.cwd });
/** @type {ConfigArray[]} */
return patterns.filter(Boolean);
}
- const extensions = options.extensions.map(ext => ext.replace(/^\./u, ""));
+ const extensions = (options.extensions || [".js"]).map(ext => ext.replace(/^\./u, ""));
const dirSuffix = `/**/*.{${extensions.join(",")}}`;
return patterns.filter(Boolean).map(pathname => {
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
- extensionRegExp: fileEnumerator.extensionRegExp,
+ fileEnumerator,
linter
});
lintResultCache.reconcile();
}
- // Collect used deprecated rules.
- const usedDeprecatedRules = Array.from(
- iterateRuleDeprecationWarnings(lastConfigArrays)
- );
-
debug(`Linting complete in: ${Date.now() - startTime}ms`);
+ let usedDeprecatedRules;
+
return {
results,
...calculateStatsPerRun(results),
- usedDeprecatedRules
+
+ // Initialize it lazily because CLI and `ESLint` API don't use it.
+ get usedDeprecatedRules() {
+ if (!usedDeprecatedRules) {
+ usedDeprecatedRules = Array.from(
+ iterateRuleDeprecationWarnings(lastConfigArrays)
+ );
+ }
+ return usedDeprecatedRules;
+ }
};
}
const startTime = Date.now();
const resolvedFilename = filename && path.resolve(cwd, filename);
+
// Clear the last used config arrays.
lastConfigArrays.length = 0;
-
if (resolvedFilename && this.isPathIgnored(resolvedFilename)) {
if (warnIgnored) {
results.push(createIgnoreResult(resolvedFilename, cwd));
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
- extensionRegExp: fileEnumerator.extensionRegExp,
+ fileEnumerator,
linter
}));
}
- // Collect used deprecated rules.
- const usedDeprecatedRules = Array.from(
- iterateRuleDeprecationWarnings(lastConfigArrays)
- );
-
debug(`Linting complete in: ${Date.now() - startTime}ms`);
+ let usedDeprecatedRules;
+
return {
results,
...calculateStatsPerRun(results),
- usedDeprecatedRules
+
+ // Initialize it lazily because CLI and `ESLint` API don't use it.
+ get usedDeprecatedRules() {
+ if (!usedDeprecatedRules) {
+ usedDeprecatedRules = Array.from(
+ iterateRuleDeprecationWarnings(lastConfigArrays)
+ );
+ }
+ return usedDeprecatedRules;
+ }
};
}
}
/**
- * Returns the formatter representing the given format or null if no formatter
- * with the given name can be found.
+ * Returns the formatter representing the given format or null if the `format` is not a string.
* @param {string} [format] The name of the format to load or the path to a
* custom formatter.
- * @returns {Function} The formatter function or null if not found.
+ * @returns {(Function|null)} The formatter function or null if the `format` is not a string.
*/
getFormatter(format) {
const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
formatterPath = ModuleResolver.resolve(npmFormat, path.join(cwd, "__placeholder__.js"));
- } catch (e) {
+ } catch {
formatterPath = path.resolve(__dirname, "formatters", normalizedFormatName);
}
}