Skip to content

Commit

Permalink
Apply gitignores as folders are traversed
Browse files Browse the repository at this point in the history
Improves performance 3x.

Also, ensure gitignore reading is done to spec: https://git-scm.com/docs/gitignore.
  • Loading branch information
Nixinova committed Mar 19, 2024
1 parent c394ba3 commit 51a7be2
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 22 deletions.
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Next
- Fixed gitignore file reading not pertaining exactly to spec.
- Improved performance by filtering out gitignored files during folder traversal.

## 2.7.0

### 2.7.0 (RC)
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/parse-gitignore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function parseGitignore(content: string): string[] {
const readableData = content
// Remove comments unless escaped
.replace(/(?<!\\)#.+/g, '')
// Remove whitespace unless escaped
.replace(/(?:(?<!\\)\s)+$/g, '')
const arrayData = readableData.split(/\r?\n/).filter(data => data);
return arrayData;
}
11 changes: 11 additions & 0 deletions src/helpers/walk-tree.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'fs';
import paths from 'path';
import ignore, { Ignore } from 'ignore';
import parseGitignore from './parse-gitignore';

let allFiles: Set<string>;
let allFolders: Set<string>;
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function walk(data: WalkInput): WalkOutput {
if (folders.length === 1) {
const folder = folders[0];
const localRoot = folderRoots[0].replace(commonRoot, '').replace(/^\//, '');

// Get list of files and folders inside this folder
const files = fs.readdirSync(folder).map(file => {
// Create path relative to root
Expand All @@ -45,6 +47,15 @@ export default function walk(data: WalkInput): WalkOutput {
const isDir = fs.lstatSync(paths.resolve(commonRoot, base)).isDirectory();
return isDir ? `${base}/` : base;
});

// Read and apply gitignores
const gitignoreFilename = paths.join(folder, '.gitignore');
if (fs.existsSync(gitignoreFilename)) {
const gitignoreContents = fs.readFileSync(gitignoreFilename, 'utf-8');
const ignoredPaths = parseGitignore(gitignoreContents);
ignored.add(ignoredPaths);
}

// Loop through files and folders
for (const file of files) {
// Create absolute path for disc operations
Expand Down
22 changes: 0 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,36 +72,14 @@ async function analyse(rawPaths?: string | string[], opts: T.Options = {}): Prom

// Load file paths and folders
let files: T.AbsFile[];
let folders: T.AbsFolder[];
if (useRawContent) {
// Uses raw file content
files = input;
folders = [''];
}
else {
// Uses directory on disc
const data = walk({ init: true, commonRoot, folderRoots: resolvedInput, folders: resolvedInput, ignored });
files = data.files;
folders = data.folders;
}

// Load gitignore data and apply ignores rules
if (!useRawContent && opts.checkIgnored) {
const nestedIgnoreFiles = files.filter(file => file.endsWith('.gitignore'));
for (const ignoresFile of nestedIgnoreFiles) {
const relIgnoresFile = relPath(ignoresFile);
const relIgnoresFolder = paths.dirname(relIgnoresFile);
// Parse gitignores
const ignoresDataRaw = await readFile(ignoresFile);
const ignoresData = ignoresDataRaw.replace(/#.+|\s+$/gm, '');
const absoluteIgnoresData = ignoresData
// '.file' -> 'root/*/.file'
.replace(/^(?=[^\s\/\\])/gm, localRoot(relIgnoresFolder) + '/*/')
// '/folder' -> 'root/folder'
.replace(/^[\/\\]/gm, localRoot(relIgnoresFolder) + '/')
ignored.add(absoluteIgnoresData);
files = filterOutIgnored(files, ignored);
}
}

// Fetch and normalise gitattributes data of all subfolders and save to metadata
Expand Down

0 comments on commit 51a7be2

Please sign in to comment.