mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Add cline-ignore utility and tests
- Implemented `loadClineIgnoreFile` to read and return contents of `.clineignore`. - Created `shouldIgnorePath` function to determine if a file path should be ignored based on patterns in the ignore file. - Added tests for `shouldIgnorePath` covering exact matches, wildcards, directory patterns, comments, and negation patterns. - Introduced new files: `src/utils/cline-ignore.ts` and `src/utils/__tests__/cline-ignore.test.ts`. Enhance cline-ignore utility with additional functions and documentation - Added `parseIgnorePatterns` function to streamline parsing of .clineignore file. - Implemented `loadIgnorePatterns` to load patterns and provide an evaluation function. - Introduced `filterIgnoredPaths` for batch filtering of file paths based on ignore patterns. - Improved documentation with JSDoc comments for better clarity on function usage.
This commit is contained in:
parent
7b668277fa
commit
5d19bbd52a
2 changed files with 160 additions and 0 deletions
73
src/utils/__tests__/cline-ignore.test.ts
Normal file
73
src/utils/__tests__/cline-ignore.test.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { shouldIgnorePath } from "../cline-ignore"
|
||||
|
||||
describe("shouldIgnorePath", () => {
|
||||
test("exact match pattern", () => {
|
||||
const ignoreContent = "test.txt"
|
||||
expect(shouldIgnorePath("test.txt", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("other.txt", ignoreContent)).toBe(false)
|
||||
})
|
||||
|
||||
test("wildcard pattern", () => {
|
||||
const ignoreContent = "*.txt"
|
||||
expect(shouldIgnorePath("test.txt", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("test.js", ignoreContent)).toBe(false)
|
||||
})
|
||||
|
||||
test("directory pattern", () => {
|
||||
const ignoreContent = "node_modules/"
|
||||
expect(shouldIgnorePath("node_modules/package.json", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("src/node_modules.ts", ignoreContent)).toBe(false)
|
||||
})
|
||||
|
||||
test("comments and empty lines", () => {
|
||||
const ignoreContent = `
|
||||
# This is a comment
|
||||
test.txt
|
||||
|
||||
# This is also ignored
|
||||
*.js
|
||||
`
|
||||
expect(shouldIgnorePath("test.txt", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("app.js", ignoreContent)).toBe(true)
|
||||
})
|
||||
|
||||
test("negation pattern", () => {
|
||||
const ignoreContent = `
|
||||
*.txt
|
||||
!important.txt
|
||||
docs/
|
||||
!docs/README.txt
|
||||
`
|
||||
// Matches *.txt but excluded by !important.txt
|
||||
expect(shouldIgnorePath("test.txt", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("important.txt", ignoreContent)).toBe(false)
|
||||
|
||||
// Matches docs/ but excluded by !docs/README.txt
|
||||
expect(shouldIgnorePath("docs/test.txt", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("docs/README.txt", ignoreContent)).toBe(false)
|
||||
})
|
||||
|
||||
test("complex negation pattern combinations", () => {
|
||||
const ignoreContent = `
|
||||
# Ignore all .log files
|
||||
*.log
|
||||
# But not debug.log
|
||||
!debug.log
|
||||
# However, ignore debug.log in tmp/
|
||||
tmp/debug.log
|
||||
`
|
||||
expect(shouldIgnorePath("error.log", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath("debug.log", ignoreContent)).toBe(false)
|
||||
expect(shouldIgnorePath("tmp/debug.log", ignoreContent)).toBe(true)
|
||||
})
|
||||
|
||||
test("negation pattern with reversed order", () => {
|
||||
const ignoreContent = `
|
||||
!.env.example
|
||||
.env*
|
||||
`
|
||||
// .env.example should be ignored because .env* comes after !.env.example
|
||||
expect(shouldIgnorePath(".env.example", ignoreContent)).toBe(true)
|
||||
expect(shouldIgnorePath(".env.local", ignoreContent)).toBe(true)
|
||||
})
|
||||
})
|
||||
87
src/utils/cline-ignore.ts
Normal file
87
src/utils/cline-ignore.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import { fileExistsAtPath } from "./fs"
|
||||
import * as path from "path"
|
||||
import * as fs from "fs/promises"
|
||||
|
||||
/**
|
||||
* Loads the contents of .clineignore file and returns cache and evaluation function.
|
||||
* @param cwd Current working directory
|
||||
* @returns Object containing patterns and evaluation function
|
||||
*/
|
||||
function parseIgnorePatterns(clineIgnoreFile: string): string[] {
|
||||
return clineIgnoreFile
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line && !line.startsWith("#"))
|
||||
}
|
||||
|
||||
export async function loadIgnorePatterns(cwd: string): Promise<{
|
||||
patterns: string[]
|
||||
shouldIgnore: (path: string) => boolean
|
||||
}> {
|
||||
const ignoreContent = await loadClineIgnoreFile(cwd)
|
||||
const patterns = parseIgnorePatterns(ignoreContent)
|
||||
return {
|
||||
patterns,
|
||||
shouldIgnore: (path: string) => shouldIgnorePath(path, ignoreContent),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters multiple file paths in batch.
|
||||
* @param paths Array of paths to filter
|
||||
* @param ignoreContent Contents of .clineignore file
|
||||
* @returns Array of filtered paths
|
||||
*/
|
||||
export function filterIgnoredPaths(paths: string[], ignoreContent: string): string[] {
|
||||
return paths.filter((path) => !shouldIgnorePath(path, ignoreContent))
|
||||
}
|
||||
|
||||
export async function loadClineIgnoreFile(cwd: string): Promise<string> {
|
||||
const filePath = path.join(cwd, ".clineignore")
|
||||
try {
|
||||
const fileExists = await fileExistsAtPath(filePath)
|
||||
if (!fileExists) {
|
||||
return ""
|
||||
}
|
||||
return fs.readFile(filePath, "utf-8")
|
||||
} catch (error) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
function convertGlobToRegExp(pattern: string): string {
|
||||
// Handle directory pattern
|
||||
if (pattern.endsWith("/")) {
|
||||
pattern = pattern + "**"
|
||||
}
|
||||
|
||||
return (
|
||||
pattern
|
||||
// Escape special characters
|
||||
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
||||
// Convert wildcard * to regex pattern
|
||||
.replace(/\*/g, ".*")
|
||||
)
|
||||
}
|
||||
|
||||
export function shouldIgnorePath(filePath: string, clineIgnoreFile: string): boolean {
|
||||
const patterns = parseIgnorePatterns(clineIgnoreFile)
|
||||
let isIgnored = false
|
||||
|
||||
// Evaluate patterns in order
|
||||
for (const pattern of patterns) {
|
||||
const isNegation = pattern.startsWith("!")
|
||||
const actualPattern = isNegation ? pattern.slice(1) : pattern
|
||||
|
||||
// Convert pattern to regex
|
||||
const regexPattern = convertGlobToRegExp(actualPattern)
|
||||
const regex = new RegExp(`^${regexPattern}$`)
|
||||
|
||||
// Check if pattern matches
|
||||
if (regex.test(filePath)) {
|
||||
isIgnored = !isNegation
|
||||
}
|
||||
}
|
||||
|
||||
return isIgnored
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue