/**
							 | 
						|
								 * @fileoverview `OverrideTester` class.
							 | 
						|
								 *
							 | 
						|
								 * `OverrideTester` class handles `files` property and `excludedFiles` property
							 | 
						|
								 * of `overrides` config.
							 | 
						|
								 *
							 | 
						|
								 * It provides one method.
							 | 
						|
								 *
							 | 
						|
								 * - `test(filePath)`
							 | 
						|
								 *      Test if a file path matches the pair of `files` property and
							 | 
						|
								 *      `excludedFiles` property. The `filePath` argument must be an absolute
							 | 
						|
								 *      path.
							 | 
						|
								 *
							 | 
						|
								 * `ConfigArrayFactory` creates `OverrideTester` objects when it processes
							 | 
						|
								 * `overrides` properties.
							 | 
						|
								 *
							 | 
						|
								 * @author Toru Nagashima <https://github.com/mysticatea>
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								import assert from "assert";
							 | 
						|
								import path from "path";
							 | 
						|
								import util from "util";
							 | 
						|
								import minimatch from "minimatch";
							 | 
						|
								
							 | 
						|
								const { Minimatch } = minimatch;
							 | 
						|
								
							 | 
						|
								const minimatchOpts = { dot: true, matchBase: true };
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @typedef {Object} Pattern
							 | 
						|
								 * @property {InstanceType<Minimatch>[] | null} includes The positive matchers.
							 | 
						|
								 * @property {InstanceType<Minimatch>[] | null} excludes The negative matchers.
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Normalize a given pattern to an array.
							 | 
						|
								 * @param {string|string[]|undefined} patterns A glob pattern or an array of glob patterns.
							 | 
						|
								 * @returns {string[]|null} Normalized patterns.
							 | 
						|
								 * @private
							 | 
						|
								 */
							 | 
						|
								function normalizePatterns(patterns) {
							 | 
						|
								    if (Array.isArray(patterns)) {
							 | 
						|
								        return patterns.filter(Boolean);
							 | 
						|
								    }
							 | 
						|
								    if (typeof patterns === "string" && patterns) {
							 | 
						|
								        return [patterns];
							 | 
						|
								    }
							 | 
						|
								    return [];
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Create the matchers of given patterns.
							 | 
						|
								 * @param {string[]} patterns The patterns.
							 | 
						|
								 * @returns {InstanceType<Minimatch>[] | null} The matchers.
							 | 
						|
								 */
							 | 
						|
								function toMatcher(patterns) {
							 | 
						|
								    if (patterns.length === 0) {
							 | 
						|
								        return null;
							 | 
						|
								    }
							 | 
						|
								    return patterns.map(pattern => {
							 | 
						|
								        if (/^\.[/\\]/u.test(pattern)) {
							 | 
						|
								            return new Minimatch(
							 | 
						|
								                pattern.slice(2),
							 | 
						|
								
							 | 
						|
								                // `./*.js` should not match with `subdir/foo.js`
							 | 
						|
								                { ...minimatchOpts, matchBase: false }
							 | 
						|
								            );
							 | 
						|
								        }
							 | 
						|
								        return new Minimatch(pattern, minimatchOpts);
							 | 
						|
								    });
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Convert a given matcher to string.
							 | 
						|
								 * @param {Pattern} matchers The matchers.
							 | 
						|
								 * @returns {string} The string expression of the matcher.
							 | 
						|
								 */
							 | 
						|
								function patternToJson({ includes, excludes }) {
							 | 
						|
								    return {
							 | 
						|
								        includes: includes && includes.map(m => m.pattern),
							 | 
						|
								        excludes: excludes && excludes.map(m => m.pattern)
							 | 
						|
								    };
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * The class to test given paths are matched by the patterns.
							 | 
						|
								 */
							 | 
						|
								class OverrideTester {
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * Create a tester with given criteria.
							 | 
						|
								     * If there are no criteria, returns `null`.
							 | 
						|
								     * @param {string|string[]} files The glob patterns for included files.
							 | 
						|
								     * @param {string|string[]} excludedFiles The glob patterns for excluded files.
							 | 
						|
								     * @param {string} basePath The path to the base directory to test paths.
							 | 
						|
								     * @returns {OverrideTester|null} The created instance or `null`.
							 | 
						|
								     */
							 | 
						|
								    static create(files, excludedFiles, basePath) {
							 | 
						|
								        const includePatterns = normalizePatterns(files);
							 | 
						|
								        const excludePatterns = normalizePatterns(excludedFiles);
							 | 
						|
								        let endsWithWildcard = false;
							 | 
						|
								
							 | 
						|
								        if (includePatterns.length === 0) {
							 | 
						|
								            return null;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Rejects absolute paths or relative paths to parents.
							 | 
						|
								        for (const pattern of includePatterns) {
							 | 
						|
								            if (path.isAbsolute(pattern) || pattern.includes("..")) {
							 | 
						|
								                throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
							 | 
						|
								            }
							 | 
						|
								            if (pattern.endsWith("*")) {
							 | 
						|
								                endsWithWildcard = true;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        for (const pattern of excludePatterns) {
							 | 
						|
								            if (path.isAbsolute(pattern) || pattern.includes("..")) {
							 | 
						|
								                throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        const includes = toMatcher(includePatterns);
							 | 
						|
								        const excludes = toMatcher(excludePatterns);
							 | 
						|
								
							 | 
						|
								        return new OverrideTester(
							 | 
						|
								            [{ includes, excludes }],
							 | 
						|
								            basePath,
							 | 
						|
								            endsWithWildcard
							 | 
						|
								        );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * Combine two testers by logical and.
							 | 
						|
								     * If either of the testers was `null`, returns the other tester.
							 | 
						|
								     * The `basePath` property of the two must be the same value.
							 | 
						|
								     * @param {OverrideTester|null} a A tester.
							 | 
						|
								     * @param {OverrideTester|null} b Another tester.
							 | 
						|
								     * @returns {OverrideTester|null} Combined tester.
							 | 
						|
								     */
							 | 
						|
								    static and(a, b) {
							 | 
						|
								        if (!b) {
							 | 
						|
								            return a && new OverrideTester(
							 | 
						|
								                a.patterns,
							 | 
						|
								                a.basePath,
							 | 
						|
								                a.endsWithWildcard
							 | 
						|
								            );
							 | 
						|
								        }
							 | 
						|
								        if (!a) {
							 | 
						|
								            return new OverrideTester(
							 | 
						|
								                b.patterns,
							 | 
						|
								                b.basePath,
							 | 
						|
								                b.endsWithWildcard
							 | 
						|
								            );
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        assert.strictEqual(a.basePath, b.basePath);
							 | 
						|
								        return new OverrideTester(
							 | 
						|
								            a.patterns.concat(b.patterns),
							 | 
						|
								            a.basePath,
							 | 
						|
								            a.endsWithWildcard || b.endsWithWildcard
							 | 
						|
								        );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * Initialize this instance.
							 | 
						|
								     * @param {Pattern[]} patterns The matchers.
							 | 
						|
								     * @param {string} basePath The base path.
							 | 
						|
								     * @param {boolean} endsWithWildcard If `true` then a pattern ends with `*`.
							 | 
						|
								     */
							 | 
						|
								    constructor(patterns, basePath, endsWithWildcard = false) {
							 | 
						|
								
							 | 
						|
								        /** @type {Pattern[]} */
							 | 
						|
								        this.patterns = patterns;
							 | 
						|
								
							 | 
						|
								        /** @type {string} */
							 | 
						|
								        this.basePath = basePath;
							 | 
						|
								
							 | 
						|
								        /** @type {boolean} */
							 | 
						|
								        this.endsWithWildcard = endsWithWildcard;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * Test if a given path is matched or not.
							 | 
						|
								     * @param {string} filePath The absolute path to the target file.
							 | 
						|
								     * @returns {boolean} `true` if the path was matched.
							 | 
						|
								     */
							 | 
						|
								    test(filePath) {
							 | 
						|
								        if (typeof filePath !== "string" || !path.isAbsolute(filePath)) {
							 | 
						|
								            throw new Error(`'filePath' should be an absolute path, but got ${filePath}.`);
							 | 
						|
								        }
							 | 
						|
								        const relativePath = path.relative(this.basePath, filePath);
							 | 
						|
								
							 | 
						|
								        return this.patterns.every(({ includes, excludes }) => (
							 | 
						|
								            (!includes || includes.some(m => m.match(relativePath))) &&
							 | 
						|
								            (!excludes || !excludes.some(m => m.match(relativePath)))
							 | 
						|
								        ));
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // eslint-disable-next-line jsdoc/require-description
							 | 
						|
								    /**
							 | 
						|
								     * @returns {Object} a JSON compatible object.
							 | 
						|
								     */
							 | 
						|
								    toJSON() {
							 | 
						|
								        if (this.patterns.length === 1) {
							 | 
						|
								            return {
							 | 
						|
								                ...patternToJson(this.patterns[0]),
							 | 
						|
								                basePath: this.basePath
							 | 
						|
								            };
							 | 
						|
								        }
							 | 
						|
								        return {
							 | 
						|
								            AND: this.patterns.map(patternToJson),
							 | 
						|
								            basePath: this.basePath
							 | 
						|
								        };
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // eslint-disable-next-line jsdoc/require-description
							 | 
						|
								    /**
							 | 
						|
								     * @returns {Object} an object to display by `console.log()`.
							 | 
						|
								     */
							 | 
						|
								    [util.inspect.custom]() {
							 | 
						|
								        return this.toJSON();
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								export { OverrideTester };
							 |