* 1. Find target files by processing glob patterns.
* 2. Tie each target file and appropriate configuration.
*
- * It provies a method:
+ * It provides a method:
*
* - `iterateFiles(patterns)`
* Iterate files which are matched by given patterns together with the
const path = require("path");
const getGlobParent = require("glob-parent");
const isGlob = require("is-glob");
-const { escapeRegExp } = require("lodash");
+const escapeRegExp = require("escape-string-regexp");
const { Minimatch } = require("minimatch");
-const { IgnorePattern } = require("./config-array");
-const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory");
+
+const {
+ Legacy: {
+ IgnorePattern,
+ CascadingConfigArrayFactory
+ }
+} = require("@eslint/eslintrc");
const debug = require("debug")("eslint:file-enumerator");
//------------------------------------------------------------------------------
* @typedef {Object} FileEnumeratorInternalSlots
* @property {CascadingConfigArrayFactory} configArrayFactory The factory for config arrays.
* @property {string} cwd The base directory to start lookup.
- * @property {RegExp} extensionRegExp The RegExp to test if a string ends with specific file extensions.
+ * @property {RegExp|null} extensionRegExp The RegExp to test if a string ends with specific file extensions.
* @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 {boolean} ignoreFlag The flag to check ignored files.
* @property {(filePath:string, dot:boolean) => boolean} defaultIgnores The default predicate function to ignore files.
/**
* Get filenames in a given path to a directory.
* @param {string} directoryPath The path to target directory.
- * @returns {string[]} The filenames.
+ * @returns {import("fs").Dirent[]} The filenames.
* @private
*/
function readdirSafeSync(directoryPath) {
try {
- return fs.readdirSync(directoryPath);
+ return fs.readdirSync(directoryPath, { withFileTypes: true });
} catch (error) {
/* istanbul ignore next */
if (error.code !== "ENOENT") {
}
}
+/**
+ * Create a `RegExp` object to detect extensions.
+ * @param {string[] | null} extensions The extensions to create.
+ * @returns {RegExp | null} The created `RegExp` object or null.
+ */
+function createExtensionRegExp(extensions) {
+ if (extensions) {
+ const normalizedExts = extensions.map(ext => escapeRegExp(
+ ext.startsWith(".")
+ ? ext.slice(1)
+ : ext
+ ));
+
+ return new RegExp(
+ `.\\.(?:${normalizedExts.join("|")})$`,
+ "u"
+ );
+ }
+ return null;
+}
+
/**
* The error type when no files match a glob.
*/
*/
constructor({
cwd = process.cwd(),
- configArrayFactory = new CascadingConfigArrayFactory({ cwd }),
- extensions = [".js"],
+ configArrayFactory = new CascadingConfigArrayFactory({
+ cwd,
+ eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
+ eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
+ }),
+ extensions = null,
globInputPaths = true,
errorOnUnmatchedPattern = true,
ignore = true
configArrayFactory,
cwd,
defaultIgnores: IgnorePattern.createDefaultIgnore(cwd),
- extensionRegExp: new RegExp(
- `.\\.(?:${extensions
- .map(ext => escapeRegExp(
- ext.startsWith(".")
- ? ext.slice(1)
- : ext
- ))
- .join("|")
- })$`,
- "u"
- ),
+ extensionRegExp: createExtensionRegExp(extensions),
globInputPaths,
errorOnUnmatchedPattern,
ignoreFlag: ignore
}
/**
- * The `RegExp` object that tests if a file path has the allowed file extensions.
- * @type {RegExp}
+ * Check if a given file is target or not.
+ * @param {string} filePath The path to a candidate file.
+ * @param {ConfigArray} [providedConfig] Optional. The configuration for the file.
+ * @returns {boolean} `true` if the file is a target.
*/
- get extensionRegExp() {
- return internalSlotsMap.get(this).extensionRegExp;
+ isTargetPath(filePath, providedConfig) {
+ const {
+ configArrayFactory,
+ extensionRegExp
+ } = internalSlotsMap.get(this);
+
+ // If `--ext` option is present, use it.
+ if (extensionRegExp) {
+ return extensionRegExp.test(filePath);
+ }
+
+ // `.js` file is target by default.
+ if (filePath.endsWith(".js")) {
+ return true;
+ }
+
+ // use `overrides[].files` to check additional targets.
+ const config =
+ providedConfig ||
+ configArrayFactory.getConfigArrayForFile(
+ filePath,
+ { ignoreNotFoundError: true }
+ );
+
+ return config.isAdditionalTargetPath(filePath);
}
/**
continue;
}
- // Iterate files of this pttern.
+ // Iterate files of this pattern.
for (const { config, filePath, flag } of this._iterateFiles(pattern)) {
foundRegardlessOfIgnored = true;
if (flag === IGNORED_SILENTLY) {
*/
*_iterateFilesRecursive(directoryPath, options) {
debug(`Enter the directory: ${directoryPath}`);
- const { configArrayFactory, extensionRegExp } = internalSlotsMap.get(this);
+ const { configArrayFactory } = internalSlotsMap.get(this);
/** @type {ConfigArray|null} */
let config = null;
// Enumerate the files of this directory.
- for (const filename of readdirSafeSync(directoryPath)) {
- const filePath = path.join(directoryPath, filename);
- const stat = statSafeSync(filePath); // TODO: Use `withFileTypes` in the future.
+ for (const entry of readdirSafeSync(directoryPath)) {
+ const filePath = path.join(directoryPath, entry.name);
+ const fileInfo = entry.isSymbolicLink() ? statSafeSync(filePath) : entry;
+
+ if (!fileInfo) {
+ continue;
+ }
// Check if the file is matched.
- if (stat && stat.isFile()) {
+ if (fileInfo.isFile()) {
if (!config) {
config = configArrayFactory.getConfigArrayForFile(
filePath,
{ ignoreNotFoundError: true }
);
}
- const ignored = this._isIgnoredFile(filePath, { ...options, config });
- const flag = ignored ? IGNORED_SILENTLY : NONE;
const matched = options.selector
// Started with a glob pattern; choose by the pattern.
? options.selector.match(filePath)
// Started with a directory path; choose by file extensions.
- : extensionRegExp.test(filePath);
+ : this.isTargetPath(filePath, config);
if (matched) {
- debug(`Yield: ${filename}${ignored ? " but ignored" : ""}`);
+ const ignored = this._isIgnoredFile(filePath, { ...options, config });
+ const flag = ignored ? IGNORED_SILENTLY : NONE;
+
+ debug(`Yield: ${entry.name}${ignored ? " but ignored" : ""}`);
yield {
config: configArrayFactory.getConfigArrayForFile(filePath),
filePath,
flag
};
} else {
- debug(`Didn't match: ${filename}`);
+ debug(`Didn't match: ${entry.name}`);
}
// Dive into the sub directory.
- } else if (options.recursive && stat && stat.isDirectory()) {
+ } else if (options.recursive && fileInfo.isDirectory()) {
if (!config) {
config = configArrayFactory.getConfigArrayForFile(
filePath,