-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
resolveProjectList.ts
150 lines (134 loc) · 4.17 KB
/
resolveProjectList.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
146
147
148
149
150
import debug from 'debug';
import { sync as globSync } from 'globby';
import isGlob from 'is-glob';
import type { CanonicalPath } from '../create-program/shared';
import {
createHash,
ensureAbsolutePath,
getCanonicalFileName,
} from '../create-program/shared';
import type { TSESTreeOptions } from '../parser-options';
import {
DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS,
ExpiringCache,
} from './ExpiringCache';
const log = debug(
'typescript-eslint:typescript-estree:parser:parseSettings:resolveProjectList',
);
let RESOLUTION_CACHE: ExpiringCache<string, readonly CanonicalPath[]> | null =
null;
export function clearGlobCache(): void {
RESOLUTION_CACHE?.clear();
}
/**
* Normalizes, sanitizes, resolves and filters the provided project paths
*/
export function resolveProjectList(
options: Readonly<{
cacheLifetime?: TSESTreeOptions['cacheLifetime'];
project: TSESTreeOptions['project'];
projectFolderIgnoreList: TSESTreeOptions['projectFolderIgnoreList'];
singleRun: boolean;
tsconfigRootDir: string;
}>,
): readonly CanonicalPath[] {
const sanitizedProjects: string[] = [];
// Normalize and sanitize the project paths
if (typeof options.project === 'string') {
sanitizedProjects.push(options.project);
} else if (Array.isArray(options.project)) {
for (const project of options.project) {
if (typeof project === 'string') {
sanitizedProjects.push(project);
}
}
}
if (sanitizedProjects.length === 0) {
return [];
}
const projectFolderIgnoreList = (
options.projectFolderIgnoreList ?? ['**/node_modules/**']
)
.reduce<string[]>((acc, folder) => {
if (typeof folder === 'string') {
acc.push(folder);
}
return acc;
}, [])
// prefix with a ! for not match glob
.map(folder => (folder.startsWith('!') ? folder : `!${folder}`));
const cacheKey = getHash({
project: sanitizedProjects,
projectFolderIgnoreList,
tsconfigRootDir: options.tsconfigRootDir,
});
if (RESOLUTION_CACHE == null) {
// note - we initialize the global cache based on the first config we encounter.
// this does mean that you can't have multiple lifetimes set per folder
// I doubt that anyone will really bother reconfiguring this, let alone
// try to do complicated setups, so we'll deal with this later if ever.
RESOLUTION_CACHE = new ExpiringCache(
options.singleRun
? 'Infinity'
: options.cacheLifetime?.glob ??
DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS,
);
} else {
const cached = RESOLUTION_CACHE.get(cacheKey);
if (cached) {
return cached;
}
}
// Transform glob patterns into paths
const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project));
const globProjects = sanitizedProjects.filter(project => isGlob(project));
const uniqueCanonicalProjectPaths = new Set(
nonGlobProjects
.concat(
globProjects.length === 0
? []
: globSync([...globProjects, ...projectFolderIgnoreList], {
cwd: options.tsconfigRootDir,
}),
)
.map(project =>
getCanonicalFileName(
ensureAbsolutePath(project, options.tsconfigRootDir),
),
),
);
log(
'parserOptions.project (excluding ignored) matched projects: %s',
uniqueCanonicalProjectPaths,
);
const returnValue = Array.from(uniqueCanonicalProjectPaths);
RESOLUTION_CACHE.set(cacheKey, returnValue);
return returnValue;
}
function getHash({
project,
projectFolderIgnoreList,
tsconfigRootDir,
}: Readonly<{
project: readonly string[];
projectFolderIgnoreList: readonly string[];
tsconfigRootDir: string;
}>): string {
// create a stable representation of the config
const hashObject = {
tsconfigRootDir,
// the project order does matter and can impact the resolved globs
project,
// the ignore order won't doesn't ever matter
projectFolderIgnoreList: [...projectFolderIgnoreList].sort(),
};
return createHash(JSON.stringify(hashObject));
}
/**
* Exported for testing purposes only
* @internal
*/
export function clearGlobResolutionCache(): void {
RESOLUTION_CACHE?.clear();
RESOLUTION_CACHE = null;
}