Loading etc/browser.api.md +2 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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; Loading etc/index.api.md +3 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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) Loading Loading @@ -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; Loading src/cli/cli.spec.ts +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"; Loading Loading @@ -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(); }); }); }); src/cli/cli.ts +39 −5 Original line number Diff line number Diff line Loading @@ -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> { Loading Loading @@ -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); } Loading src/config/config-loader.ts +17 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
etc/browser.api.md +2 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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; Loading
etc/index.api.md +3 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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) Loading Loading @@ -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; Loading
src/cli/cli.spec.ts +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"; Loading Loading @@ -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(); }); }); });
src/cli/cli.ts +39 −5 Original line number Diff line number Diff line Loading @@ -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> { Loading Loading @@ -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); } Loading
src/config/config-loader.ts +17 −0 Original line number Diff line number Diff line Loading @@ -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