import type { LintKind, SourceDialect } from "./types.ts" const SLASH_EXTENSIONS = new Set([ "ts", "tsx", "js", "jsx", "mjs", "cjs", "go", "rs", "java", "c", "h", "cpp", "hpp", "cc", "cs", "swift", "kt", "scala", ]) const JAVASCRIPT_EXTENSIONS = new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs"]) const NESTED_SLASH_EXTENSIONS = new Set(["rs", "swift", "kt", "scala"]) const HASH_EXTENSIONS = new Set(["sh", "bash", "zsh", "py", "rb", "yaml", "yml", "toml", "pl"]) const HTML_EXTENSIONS = new Set(["html", "htm"]) // Data and style extensions carry rules and structured data rather than prose, // so the writing rules would misread them as sentences (HUF-308). const SKIP_EXTENSIONS = new Set([ "css", "scss", "less", "json", "jsonc", "svg", "xml", "typ", "csv", "tsv", "lock", ]) export interface PathClassification { readonly kind: LintKind readonly sourceDialect: SourceDialect readonly skipped?: true } export const classifyPath = (path: string): PathClassification => { const dot = path.lastIndexOf(".") if (dot <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"))) { return { kind: "prose-file", sourceDialect: "general" } } const extension = path.slice(dot + 1).toLowerCase() if (SKIP_EXTENSIONS.has(extension)) { return { kind: "prose-file", sourceDialect: "general", skipped: true } } if (HTML_EXTENSIONS.has(extension)) { return { kind: "html", sourceDialect: "general" } } if (SLASH_EXTENSIONS.has(extension)) { const sourceDialect = JAVASCRIPT_EXTENSIONS.has(extension) ? "javascript" : NESTED_SLASH_EXTENSIONS.has(extension) ? "nested-slash" : "general" return { kind: "slash-source", sourceDialect } } if (HASH_EXTENSIONS.has(extension)) { const sourceDialect = ["sh", "bash", "zsh"].includes(extension) ? "shell" : ["yaml", "yml"].includes(extension) ? "yaml" : extension === "rb" ? "ruby" : extension === "pl" ? "perl" : "general" return { kind: "hash-source", sourceDialect } } return { kind: "prose-file", sourceDialect: "general" } }