Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add method getConfigStatus, update isFileIgnored #7

Merged
merged 6 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 70 additions & 25 deletions packages/config-array/src/config-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ const META_FIELDS = new Set(["name"]);
*/
const FILES_AND_IGNORES_SCHEMA = new ObjectSchema(filesAndIgnoresSchema);

// Precomputed constant objects returned by `ConfigArray.getConfigWithStatus`.

const CONFIG_WITH_STATUS_EXTERNAL = Object.freeze({ status: "external" });
const CONFIG_WITH_STATUS_IGNORED = Object.freeze({ status: "ignored" });
const CONFIG_WITH_STATUS_UNCONFIGURED = Object.freeze({
status: "unconfigured",
});

/**
* Wrapper error for config validation errors that adds a name to the front of the
* error message.
Expand Down Expand Up @@ -820,11 +828,14 @@ export class ConfigArray extends Array {
}

/**
* Returns the config object for a given file path.
* Returns the config object for a given file path and a status that can be used to determine why a file has no config.
* @param {string} filePath The complete path of a file to get a config for.
* @returns {Object} The config object for this file.
* @returns {{ config?: Object, status: "ignored"|"external"|"unconfigured"|"matched" }}
* An object with an optional property `config` and property `status`.
* `config` is the config object for the specified file as returned by {@linkcode ConfigArray.getConfig},
* `status` a is one of the constants returned by {@linkcode ConfigArray.getConfigStatus}.
*/
getConfig(filePath) {
getConfigWithStatus(filePath) {
assertNormalized(this);

const cache = this[ConfigArraySymbol.configCache];
Expand All @@ -834,28 +845,35 @@ export class ConfigArray extends Array {
return cache.get(filePath);
}

let finalConfig;
// check to see if the file is outside the base path

const relativeFilePath = path.relative(this.basePath, filePath);

if (relativeFilePath.startsWith("..")) {
debug(`No config for file ${filePath} outside of base path`);

// cache and return result
cache.set(filePath, CONFIG_WITH_STATUS_EXTERNAL);
return CONFIG_WITH_STATUS_EXTERNAL;
}

// next check to see if the file should be ignored

// check if this should be ignored due to its directory
if (this.isDirectoryIgnored(path.dirname(filePath))) {
debug(`Ignoring ${filePath} based on directory pattern`);

// cache and return result - finalConfig is undefined at this point
cache.set(filePath, finalConfig);
return finalConfig;
// cache and return result
cache.set(filePath, CONFIG_WITH_STATUS_IGNORED);
return CONFIG_WITH_STATUS_IGNORED;
}

// TODO: Maybe move elsewhere?
const relativeFilePath = path.relative(this.basePath, filePath);

if (shouldIgnorePath(this.ignores, filePath, relativeFilePath)) {
debug(`Ignoring ${filePath} based on file pattern`);

// cache and return result - finalConfig is undefined at this point
cache.set(filePath, finalConfig);
return finalConfig;
// cache and return result
cache.set(filePath, CONFIG_WITH_STATUS_IGNORED);
return CONFIG_WITH_STATUS_IGNORED;
}

// filePath isn't automatically ignored, so try to construct config
Expand Down Expand Up @@ -949,24 +967,25 @@ export class ConfigArray extends Array {
if (!matchFound) {
debug(`No matching configs found for ${filePath}`);

// cache and return result - finalConfig is undefined at this point
cache.set(filePath, finalConfig);
return finalConfig;
// cache and return result
cache.set(filePath, CONFIG_WITH_STATUS_UNCONFIGURED);
return CONFIG_WITH_STATUS_UNCONFIGURED;
}

// check to see if there is a config cached by indices
finalConfig = cache.get(matchingConfigIndices.toString());
const indicesKey = matchingConfigIndices.toString();
let configWithStatus = cache.get(indicesKey);

if (finalConfig) {
if (configWithStatus) {
// also store for filename for faster lookup next time
cache.set(filePath, finalConfig);
cache.set(filePath, configWithStatus);

return finalConfig;
return configWithStatus;
}

// otherwise construct the config

finalConfig = matchingConfigIndices.reduce((result, index) => {
let finalConfig = matchingConfigIndices.reduce((result, index) => {
try {
return this[ConfigArraySymbol.schema].merge(
result,
Expand All @@ -979,10 +998,36 @@ export class ConfigArray extends Array {

finalConfig = this[ConfigArraySymbol.finalizeConfig](finalConfig);

cache.set(filePath, finalConfig);
cache.set(matchingConfigIndices.toString(), finalConfig);
configWithStatus = Object.freeze({
config: finalConfig,
status: "matched",
});
cache.set(filePath, configWithStatus);
cache.set(indicesKey, configWithStatus);

return configWithStatus;
}

return finalConfig;
/**
* Returns the config object for a given file path.
* @param {string} filePath The complete path of a file to get a config for.
* @returns {Object|undefined} The config object for this file or `undefined`.
*/
getConfig(filePath) {
return this.getConfigWithStatus(filePath).config;
}

/**
* Determines whether a file has a config or why it doesn't.
* @param {string} filePath The complete path of the file to check.
* @returns {"ignored"|"external"|"unconfigured"|"matched"} One of the following values:
* * `"ignored"`: the file is ignored
* * `"external"`: the file is outside the base path
* * `"unconfigured"`: the file is not matched by any config
* * `"matched"`: the file has a matching config
*/
getConfigStatus(filePath) {
return this.getConfigWithStatus(filePath).status;
}

/**
Expand All @@ -1001,7 +1046,7 @@ export class ConfigArray extends Array {
* @returns {boolean} True if the path is ignored, false if not.
*/
isFileIgnored(filePath) {
return this.getConfig(filePath) === undefined;
return this.getConfigStatus(filePath) === "ignored";
}
Comment on lines 1048 to 1050
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ESLint, we'll need to update all uses of isFileIgnored() to getConfig() or getConfigStatus() in order to retain current functionalities, which I think raises the question of whether isFileIgnored is needed anymore. I think we should just remove it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still like it as a shorthand for getConfigStatus() === "ignored".


/**
Expand Down
Loading