Commit a28e069f authored by David Sveningsson's avatar David Sveningsson
Browse files

feat: expose `isIgnored()` method

parent d05fc2b6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@ export abstract class ConfigLoader {
    protected getGlobalConfigSync(): Config;
    // @internal (undocumented)
    getResolvers(): Resolver[];
    isIgnored?(handle: string): boolean | Promise<boolean>;
    protected loadFromFile(filename: string): Config | Promise<Config>;
    protected loadFromObject(options: ConfigData, filename?: string | null): Config | Promise<Config>;
    // (undocumented)
@@ -630,6 +631,7 @@ export class HtmlValidate {
    getRuleDocumentation(ruleId: string, config?: ResolvedConfig | Promise<ResolvedConfig> | null, context?: unknown | null): Promise<RuleDocumentation | null>;
    // @internal @deprecated
    getRuleDocumentationSync(ruleId: string, config?: ResolvedConfig | null, context?: unknown | null): RuleDocumentation | null;
    isIgnored(filename: string): Promise<boolean>;
    setConfigLoader(loader: ConfigLoader): void;
    // @internal
    startPerformance(): void;
+3 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ export class CLI {
    getLoader(): Promise<ConfigLoader>;
    getValidator(): Promise<HtmlValidate>;
    init(cwd: string): Promise<InitResult>;
    isIgnored(filename: string): Promise<boolean>;
}

// @public (undocumented)
@@ -214,6 +215,7 @@ export abstract class ConfigLoader {
    protected getGlobalConfigSync(): Config;
    // @internal (undocumented)
    getResolvers(): Resolver[];
    isIgnored?(handle: string): boolean | Promise<boolean>;
    protected loadFromFile(filename: string): Config | Promise<Config>;
    protected loadFromObject(options: ConfigData, filename?: string | null): Config | Promise<Config>;
    // (undocumented)
@@ -734,6 +736,7 @@ export class HtmlValidate {
    getRuleDocumentation(ruleId: string, config?: ResolvedConfig | Promise<ResolvedConfig> | null, context?: unknown | null): Promise<RuleDocumentation | null>;
    // @internal @deprecated
    getRuleDocumentationSync(ruleId: string, config?: ResolvedConfig | null, context?: unknown | null): RuleDocumentation | null;
    isIgnored(filename: string): Promise<boolean>;
    setConfigLoader(loader: ConfigLoader): void;
    // @internal
    startPerformance(): void;
+42 −0
Original line number Diff line number Diff line
import path from "node:path";
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { vol } from "memfs";
import { type ResolvedConfig, Config, ConfigLoader } from "../config";
import { FileSystemConfigLoader } from "../config/loaders/file-system";
import { FlatConfigLoader } from "../flat-config";
import { CLI } from "./cli";
@@ -303,4 +304,45 @@ describe("CLI", () => {
			expect(fromDirSpy).not.toHaveBeenCalled();
		});
	});

	describe("isIgnored()", () => {
		it("should delegate to configuration loader isIgnored() when supported", async () => {
			expect.assertions(2);
			class MockLoader extends ConfigLoader {
				public override isIgnored = jest
					.fn<(handle?: string) => Promise<boolean>>()
					.mockResolvedValue(true);
				public defaultConfig(): Config {
					return Config.empty();
				}
				public flushCache(): void {
					/* do nothing */
				}
				public getConfigFor(): Promise<ResolvedConfig> {
					return Promise.resolve(Config.empty().resolve());
				}
			}
			const loader = new MockLoader([]);
			const cli = new CLI();
			jest.spyOn(cli, "getLoader").mockResolvedValue(loader);
			expect(await cli.isIgnored("my-file.html")).toBeTruthy();
			expect(loader.isIgnored).toHaveBeenCalledWith("my-file.html");
		});

		it("should fall back to .htmlvalidateignore when loader does not implement isIgnored()", async () => {
			expect.assertions(2);
			vol.fromJSON(
				{
					"package.json": "{}",
					".htmlvalidateignore": "ignored.html\n",
					"ignored.html": "",
					"not-ignored.html": "",
				},
				"/folder",
			);
			const cli = new CLI();
			expect(await cli.isIgnored("/folder/ignored.html")).toBeTruthy();
			expect(await cli.isIgnored("/folder/not-ignored.html")).toBeFalsy();
		});
	});
});
+39 −5
Original line number Diff line number Diff line
@@ -73,9 +73,15 @@ export class CLI {
	 *
	 * @public
	 */
	/* eslint-disable-next-line @typescript-eslint/require-await -- technical debt: expandFiles(..) should actually be async as well */
	public async expandFiles(patterns: string[], options: ExpandOptions = {}): Promise<string[]> {
		return expandFiles(patterns, options).filter((filename) => !this.isIgnored(filename));
		const result = [] as string[];
		for (const filename of expandFiles(patterns, options)) {
			if (await this.isIgnored(filename)) {
				continue;
			}
			result.push(filename);
		}
		return result;
	}

	public getFormatter(formatters: string): Promise<(report: Report) => string> {
@@ -151,10 +157,38 @@ export class CLI {
	}

	/**
	 * Searches ".htmlvalidateignore" files from filesystem and returns `true` if
	 * one of them contains a pattern matching given filename.
	 * Resolves to `true` if the given filename is ignored by the configuration.
	 *
	 * @example
	 *
	 * ```ts
	 * for (const filename of filenames) {
	 *   if (await cli.isIgnored(filename)) {
	 *     continue;
	 *   }
	 *   htmlvalidate.validateFile(filename);
	 * }
	 * ```
	 *
	 * @remarks
	 *
	 * If the configuration loader does not implement `isIgnored()` this method
	 * falls back to using the `.htmlvalidateignore` file.
	 *
	 * @public
	 * @since %version%
	 * @param filename - Filename to test if it is ignored.
	 * @returns A promise resolving to `true` if the given filename is ignored.
	 */
	private isIgnored(filename: string): boolean {
	public async isIgnored(filename: string): Promise<boolean> {
		/* if the loader supports the `isIgnored()` method we delegate the operation
		 * entirely to the loader. */
		const loader = await this.getLoader();
		if (loader.isIgnored) {
			return loader.isIgnored(filename);
		}

		/* fallback to using the `.htmlvalidateignore` file */
		return this.ignored.isIgnored(filename);
	}

+17 −0
Original line number Diff line number Diff line
@@ -109,6 +109,23 @@ export abstract class ConfigLoader {
		return this.resolvers;
	}

	/**
	 * Resolves to `true` if the given handle is ignored by the configuration.
	 *
	 * If this method is not implemented by the concrete loader, no files should
	 * be assumed to be ignored.
	 *
	 * Ignored is different from testing if the configuration can validate a
	 * handle or not, e.g. the configuration might support validating `*.vue` but
	 * a specific filename or directory might be configured to be ignored.
	 *
	 * @public
	 * @since %version%
	 * @param handle - Unique handle to test if it is ignored.
	 * @returns `true` or a promise resolving to `true` if the given handle is ignored.
	 */
	public isIgnored?(handle: string): boolean | Promise<boolean>;

	/**
	 * Flush configuration cache.
	 *
Loading