diff --git a/src/config.ts b/src/config.ts index 0c297f8..cd664a2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,6 @@ +// Loads the plugin's per-repo config file and extracts per-rule enable/disable +// overrides. Missing, unreadable, or malformed config falls back to defaults. + import {readFile} from 'node:fs/promises' export type PluginConfig = { @@ -29,6 +32,8 @@ export async function loadConfig(configPath: string, knownRuleIds: ReadonlySet): Map { const result = new Map() if (!parsed || typeof parsed !== 'object') return result diff --git a/src/rules/filename-alt-text.ts b/src/rules/filename-alt-text.ts index 4944b8f..5a84b60 100644 --- a/src/rules/filename-alt-text.ts +++ b/src/rules/filename-alt-text.ts @@ -1,3 +1,5 @@ +// filename-alt-text — flags alt text that is just an image filename (e.g. "hero.png"). + import type {Rule, RuleResult, RuleContext} from '../types.js' const FILENAME_PATTERN = /\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i diff --git a/src/rules/missing-alt-text.ts b/src/rules/missing-alt-text.ts index e30eb34..0277f86 100644 --- a/src/rules/missing-alt-text.ts +++ b/src/rules/missing-alt-text.ts @@ -1,3 +1,6 @@ +// missing-alt-text — flags images whose alt attribute is missing or whitespace-only. +// alt="" is treated as an intentional decorative marker and left alone. + import type {Rule, RuleResult, RuleContext} from '../types.js' export const missingAltText: Rule = { diff --git a/src/rules/placeholder-alt-text.ts b/src/rules/placeholder-alt-text.ts index fe1310e..480667a 100644 --- a/src/rules/placeholder-alt-text.ts +++ b/src/rules/placeholder-alt-text.ts @@ -1,15 +1,38 @@ +// placeholder-alt-text — flags alt text left as placeholder/boilerplate (e.g. "TODO", "alt text"). + import type {Rule, RuleResult, RuleContext} from '../types.js' import {normalizeAltText} from '../utils/normalize-alt-text.js' // Known placeholder/boilerplate strings that signal the alt text was never written. const PLACEHOLDER_ALT_TEXT = new Set([ + // Author-facing stubs 'todo', 'tbd', + 'fixme', 'placeholder', 'alt text', - 'insert alt text', 'image alt', - 'fixme', + + // "Fill me in" template prompts + 'insert alt text', + 'insert image', + 'insert photo', + 'image goes here', + 'photo goes here', + 'picture goes here', + 'your image here', + 'your photo here', + + // Dummy / default values + 'sample', + 'example', + 'test', + 'demo', + 'default', + 'untitled', + 'null', + 'undefined', + 'none', ]) export const placeholderAltText: Rule = { diff --git a/src/rules/repeated-alt-text.ts b/src/rules/repeated-alt-text.ts index b66d9f3..8dee2ed 100644 --- a/src/rules/repeated-alt-text.ts +++ b/src/rules/repeated-alt-text.ts @@ -1,3 +1,6 @@ +// repeated-alt-text — flags runs of adjacent images that share identical alt text, +// which usually means one caption was copied across a group of distinct images. + import type {ImageRecord, Rule, RuleResult} from '../types.js' import {normalizeAltText} from '../utils/normalize-alt-text.js' diff --git a/src/rules/vague-alt-text.ts b/src/rules/vague-alt-text.ts index 210a228..3c697e8 100644 --- a/src/rules/vague-alt-text.ts +++ b/src/rules/vague-alt-text.ts @@ -1,3 +1,6 @@ +// vague-alt-text — flags alt text that is a single generic word or phrase +// (e.g. "image", "logo", "photo of") carrying no real description of the image. + import type {Rule, RuleResult} from '../types.js' import {normalizeAltText} from '../utils/normalize-alt-text.js' @@ -58,18 +61,6 @@ const VAGUE_WORDS = new Set([ 'map', 'maps', - // Placeholders - // Note: todo, tbd, fixme, and placeholder live in placeholder-alt-text.ts. - 'sample', - 'example', - 'test', - 'demo', - 'default', - 'untitled', - 'null', - 'undefined', - 'none', - // File format / extension names 'jpg', 'jpeg', @@ -111,13 +102,6 @@ const VAGUE_PHRASES = new Set([ 'picture of', 'graphic of', 'screenshot of', - 'image goes here', - 'photo goes here', - 'picture goes here', - 'your image here', - 'your photo here', - 'insert image', - 'insert photo', ]) export const vagueAltText: Rule = { diff --git a/src/utils/normalize-alt-text.ts b/src/utils/normalize-alt-text.ts index 2e27391..8694833 100644 --- a/src/utils/normalize-alt-text.ts +++ b/src/utils/normalize-alt-text.ts @@ -1,8 +1,6 @@ -/** - * Normalizes alt text for comparison by trimming, lowercasing, collapsing internal - * whitespace, and stripping leading and trailing punctuation. Used by rules that - * need to compare alt text against a set. - */ +// Normalizes alt text for comparison by trimming, lowercasing, collapsing internal +// whitespace, and stripping leading and trailing punctuation. Used by rules that +// compare alt text against a fixed set of words or phrases. export function normalizeAltText(alt: string): string { return alt .trim() diff --git a/tests/unit/placeholder-alt-text.test.ts b/tests/unit/placeholder-alt-text.test.ts index 36a4901..b944ce2 100644 --- a/tests/unit/placeholder-alt-text.test.ts +++ b/tests/unit/placeholder-alt-text.test.ts @@ -2,7 +2,31 @@ import {describe, it, expect} from 'vitest' import {placeholderAltText} from '../../src/rules/placeholder-alt-text.js' import {evaluateAlts} from '../utils/helpers.js' -const placeholders = ['todo', 'tbd', 'placeholder', 'alt text', 'insert alt text', 'image alt', 'fixme'] +const placeholders = [ + 'todo', + 'tbd', + 'fixme', + 'placeholder', + 'alt text', + 'image alt', + 'insert alt text', + 'insert image', + 'insert photo', + 'image goes here', + 'photo goes here', + 'picture goes here', + 'your image here', + 'your photo here', + 'sample', + 'example', + 'test', + 'demo', + 'default', + 'untitled', + 'null', + 'undefined', + 'none', +] describe('placeholder-alt-text', () => { for (const placeholder of placeholders) { diff --git a/tests/unit/vague-alt-text.test.ts b/tests/unit/vague-alt-text.test.ts index e15f88e..8431716 100644 --- a/tests/unit/vague-alt-text.test.ts +++ b/tests/unit/vague-alt-text.test.ts @@ -5,15 +5,12 @@ import {evaluateAlts, makeImage} from '../utils/helpers.js' describe('vagueAltText', () => { describe('flags vague single-word alt text', () => { - it.each(['image', 'photo', 'picture', 'icon', 'logo', 'chart', 'screenshot', 'untitled', 'below'])( - 'flags alt="%s"', - alt => { - const results = evaluateAlts([alt], vagueAltText) - expect(results).toHaveLength(1) - expect(results[0]).toBeDefined() - expect(results[0]!.image.alt).toBe(alt) - }, - ) + it.each(['image', 'photo', 'picture', 'icon', 'logo', 'chart', 'screenshot', 'below'])('flags alt="%s"', alt => { + const results = evaluateAlts([alt], vagueAltText) + expect(results).toHaveLength(1) + expect(results[0]).toBeDefined() + expect(results[0]!.image.alt).toBe(alt) + }) }) describe('flags vague multi-word phrases', () => { @@ -77,6 +74,11 @@ describe('vagueAltText', () => { expect(evaluateAlts(['tbd'], vagueAltText)).toHaveLength(0) expect(evaluateAlts(['fixme'], vagueAltText)).toHaveLength(0) expect(evaluateAlts(['placeholder'], vagueAltText)).toHaveLength(0) + expect(evaluateAlts(['untitled'], vagueAltText)).toHaveLength(0) + expect(evaluateAlts(['sample'], vagueAltText)).toHaveLength(0) + expect(evaluateAlts(['test'], vagueAltText)).toHaveLength(0) + expect(evaluateAlts(['insert image'], vagueAltText)).toHaveLength(0) + expect(evaluateAlts(['image goes here'], vagueAltText)).toHaveLength(0) }) })