-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
ignore.ts
145 lines (125 loc) · 4.19 KB
/
ignore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import * as fastGlob from 'fast-glob';
import * as fs from 'fs';
import ignore, { Ignore } from 'ignore';
import * as path from 'path';
import { Tree } from '../generators/tree';
import { normalizePath } from './path';
import { workspaceRoot } from './workspace-root';
const workspaceIgnoreFiles = ['.gitignore', '.nxignore'];
export function createNxIgnore(root: string = workspaceRoot): Ignore {
return createIgnore(root, ['.nxignore']);
}
export function createGitIgnoreFromTree(tree: Tree): Ignore {
return createIgnoreFromTree(tree, ['.gitignore']);
}
export function readWorkspaceIgnorePatterns(root = workspaceRoot): string[] {
return combineIgnorePatterns(
workspaceIgnoreFiles.flatMap((ignoreFile) =>
readIgnoreFiles(root, ignoreFile)
)
);
}
export function createIgnoreFromPatterns(patterns: string[]): Ignore {
const ig = ignore();
ig.add(patterns);
return ig;
}
/**
* Reads ignore files from the file system and returns an object which can be
* used to check whether a file should be ignored.
*
* @param rootDir The directory in which to start searching for ignore files.
* Paths evaluated by the returned object must be relative to this directory.
* Defaults to the workspace root.
* @param ignoreFiles The filename of ignore files to include, e.g. ".gitignore".
* Defaults to [".gitignore", ".nxignore"].
*/
export function createIgnore(
rootDir = workspaceRoot,
ignoreFiles = workspaceIgnoreFiles
): Ignore {
return createCombinedIgnore(
ignoreFiles.flatMap((ignoreFile) => readIgnoreFiles(rootDir, ignoreFile))
);
}
/**
* Reads ignore files from a Tree and returns an object which can be
* used to check whether a file should be ignored.
*
* @param tree The tree in which to searching for ignore files.
* Paths evaluated by the returned object must be relative to the tree root.
* @param ignoreFiles The filename of ignore files to include, e.g. ".gitignore".
* Defaults to [".gitignore", ".nxignore"].
*/
export function createIgnoreFromTree(
tree: Tree,
ignoreFiles = workspaceIgnoreFiles
): Ignore {
return createCombinedIgnore(
ignoreFiles.flatMap((ignoreFile) =>
readIgnoreFilesFromTree(tree, ignoreFile)
)
);
}
interface IgnoreFile {
/**
* Path to the ignore file, relative to the root directory.
*/
path: string;
/**
* Content of the ignore file.
*/
content: string;
}
function readIgnoreFiles(rootDir: string, ignoreFile: string): IgnoreFile[] {
const ignoreFilePaths = fastGlob.sync(`**/${ignoreFile}`, {
cwd: rootDir,
dot: true,
});
return ignoreFilePaths.map((filePath) => ({
path: filePath,
content: fs.readFileSync(path.join(rootDir, filePath), 'utf-8'),
}));
}
function readIgnoreFilesFromTree(tree: Tree, ignoreFile: string): IgnoreFile[] {
const remainingPaths = ['.'];
const ignoreFiles: IgnoreFile[] = [];
while (remainingPaths.length > 0) {
const currentPath = remainingPaths.pop();
for (const child of tree.children(currentPath)) {
const childPath = normalizePath(path.join(currentPath, child));
if (tree.isFile(childPath)) {
if (child === ignoreFile) {
ignoreFiles.push({
path: childPath,
content: tree.read(childPath, 'utf-8'),
});
}
} else {
remainingPaths.push(childPath);
}
}
}
return ignoreFiles;
}
export function createCombinedIgnore(ignoreFiles: IgnoreFile[]): Ignore {
return createIgnoreFromPatterns(combineIgnorePatterns(ignoreFiles));
}
function combineIgnorePatterns(ignoreFiles: IgnoreFile[]): string[] {
return ignoreFiles.flatMap(parseIgnoreFile);
}
function parseIgnoreFile(file: IgnoreFile): string[] {
const base = normalizePath(path.dirname(file.path));
return file.content
.split(/\r?\n/)
.filter((line) => line && !line.startsWith('#'))
.map((pattern) => applyBaseToPattern(pattern, base));
}
function applyBaseToPattern(pattern: string, base: string): string {
return isNegativePattern(pattern)
? '!' + path.posix.join(base, pattern.slice(1))
: path.posix.join(base, pattern);
}
function isNegativePattern(pattern: string): boolean {
return pattern[0] === '!';
}