UNPKG

cspell-gitignore

Version:
125 lines 3.86 kB
import { promises as fs } from 'node:fs'; import * as path from 'node:path'; import { GlobMatcher } from 'cspell-glob'; import { isDefined, isParentOf, makeRelativeTo } from './helpers.js'; /** * Represents an instance of a .gitignore file. */ export class GitIgnoreFile { matcher; gitignore; constructor(matcher, gitignore) { this.matcher = matcher; this.gitignore = gitignore; } get root() { return this.matcher.root; } isIgnored(file) { return this.matcher.match(file); } isIgnoredEx(file) { const m = this.matcher.matchEx(file); const { matched } = m; const partial = m; const pattern = partial.pattern; const glob = pattern?.rawGlob ?? partial.glob; const root = partial.root; const line = pattern?.line; return { glob, matched, gitIgnoreFile: this.gitignore, root, line }; } getGlobPatters() { return this.matcher.patterns; } getGlobs(relativeTo) { return this.getGlobPatters() .map((pat) => globToString(pat, relativeTo)) .filter(isDefined); } static parseGitignore(content, gitignoreFilename) { const options = { root: path.dirname(gitignoreFilename) }; const globs = content .split(/\r?\n/g) .map((glob, index) => ({ glob: glob.replace(/^#.*/, ''), source: gitignoreFilename, line: index + 1, })) .filter((g) => !!g.glob); const globMatcher = new GlobMatcher(globs, options); return new GitIgnoreFile(globMatcher, gitignoreFilename); } static async loadGitignore(gitignore) { gitignore = path.resolve(gitignore); const content = await fs.readFile(gitignore, 'utf8'); return this.parseGitignore(content, gitignore); } } /** * A collection of nested GitIgnoreFiles to be evaluated from top to bottom. */ export class GitIgnoreHierarchy { gitIgnoreChain; constructor(gitIgnoreChain) { this.gitIgnoreChain = gitIgnoreChain; mustBeHierarchical(gitIgnoreChain); } isIgnored(file) { for (const git of this.gitIgnoreChain) { if (git.isIgnored(file)) return true; } return false; } /** * Check to see which `.gitignore` file ignored the given file. * @param file - fsPath to check. * @returns IsIgnoredExResult of the match or undefined if there was no match. */ isIgnoredEx(file) { for (const git of this.gitIgnoreChain) { const r = git.isIgnoredEx(file); if (r.matched) return r; } return undefined; } getGlobPatters() { return this.gitIgnoreChain.flatMap((gf) => gf.getGlobPatters()); } getGlobs(relativeTo) { return this.gitIgnoreChain.flatMap((gf) => gf.getGlobs(relativeTo)); } } export async function loadGitIgnore(dir) { const file = path.join(dir, '.gitignore'); try { return await GitIgnoreFile.loadGitignore(file); } catch { return undefined; } } function mustBeHierarchical(chain) { let root = ''; for (const file of chain) { if (!file.root.startsWith(root)) { throw new Error('Hierarchy violation - files are not nested'); } root = file.root; } } function globToString(glob, relativeTo) { if (glob.isGlobalPattern) return glob.glob; if (isParentOf(glob.root, relativeTo) && glob.glob.startsWith('**/')) return glob.glob; const base = makeRelativeTo(glob.root, relativeTo); if (base === undefined) return undefined; return (base ? base + '/' : '') + glob.glob; } export const __testing__ = { mustBeHierarchical, }; //# sourceMappingURL=GitIgnoreFile.js.map