@adguard/aglint
Version:
Universal adblock filter list linter.
77 lines (74 loc) • 3.26 kB
JavaScript
/*
* AGLint v3.0.0 (build date: Wed, 21 May 2025 13:24:14 GMT)
* (c) 2025 AdGuard Software Ltd.
* Released under the MIT license
* https://github.com/AdguardTeam/AGLint#readme
*/
import cloneDeep from 'clone-deep';
import { mergeConfigs } from '../config.js';
import { configFinder } from './config-finder.js';
import { parseConfigFile } from './config-reader.js';
import { NoConfigError } from './errors/no-config-error.js';
/**
* Constructs a config object for the given directory by merging all config files
* from the root config file to the actual directory. It searches all config files
* in the upper directories until it finds a root config file or the actual directory
* is the root directory, then it merges the config files from the root directory
* to the actual directory after each other. This will results the corresponding
* config object for the given directory. Works same as ESLint.
*
* @param dir Base directory
* @returns Config object
* @throws If no config file was found
* @throws If a config file is found, but it is not valid
* @throws If multiple config files are found in the same directory
*/
async function buildConfigForDirectory(dir) {
const parsedConfigs = [];
// Finds config files from the actual directory to the root directory.
// It starts from the actual directory and goes up to the root directory
// until it finds a root config file or the actual directory is the root directory.
//
// In other words, it pushes the config files to the `parsedConfigs` array
// in this order:
// - actual directory
// - parent directory
// - ...
// - root config
//
// (Of course, if the actual directory contains a root config file or the
// actual directory is the root directory, then only 1 config file will be
// pushed to the array - but the philosophy is the same.)
await configFinder(dir, async (path) => {
const parsedConfig = await parseConfigFile(path);
parsedConfigs.push(parsedConfig);
// Abort the search if the config file is a root config file
return parsedConfig.root === true;
});
// If no config file was found, throw an error, because we definitely need a config file
if (parsedConfigs.length === 0) {
throw new NoConfigError(dir);
}
// If there is only one config file, just return it, no need to do anything else
if (parsedConfigs.length === 1) {
return parsedConfigs[0];
}
// We should merge the config files from the root directory to the actual directory.
// In other words, we should merge the config files in this order:
// - root config
// - ...
// - parent directory
// - actual directory
//
// But we pushed the config files to the `parsedConfigs` array in the following order:
// - actual directory
// - parent directory
// - ...
// - root config
//
// So we have to reverse the array first, then merge the config files in the reversed order.
const mergedConfig = parsedConfigs.reduceRight((prev, curr) => mergeConfigs(prev, curr));
// Make sure that the config object is not mutated
return cloneDeep(mergedConfig);
}
export { buildConfigForDirectory };