Skip to content

Commit bdc8ef7

Browse files
committed
JS: Add tsconfig class
1 parent 56920d0 commit bdc8ef7

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Provides a class for working with `tsconfig.json` files.
3+
*/
4+
5+
private import javascript
6+
7+
/**
8+
* A TypeScript configuration file, usually named `tsconfig.json`.
9+
*/
10+
class TSConfig extends JsonObject {
11+
TSConfig() {
12+
this.getJsonFile().getBaseName().matches("%tsconfig%.json") and
13+
this.isTopLevel()
14+
}
15+
16+
/** Gets the folder containing this file. */
17+
Folder getFolder() { result = this.getJsonFile().getParentContainer() }
18+
19+
/** Gets the `compilerOptions` object. */
20+
JsonObject getCompilerOptions() { result = this.getPropValue("compilerOptions") }
21+
22+
/** Gets the string value in the `extends` property. */
23+
string getExtendsPath() { result = this.getPropStringValue("extends") }
24+
25+
/** Gets the file referred to by the `extends` property. */
26+
File getExtendedFile() { result = Resolver::resolve(this.getFolder(), this.getExtendsPath()) }
27+
28+
/** Gets the `TSConfig` file referred to by the `extends` property. */
29+
TSConfig getExtendedTSConfig() { result.getJsonFile() = this.getExtendedFile() }
30+
31+
/** Gets the string value in the `baseUrl` property. */
32+
string getBaseUrlPath() { result = this.getCompilerOptions().getPropStringValue("baseUrl") }
33+
34+
/** Gets the folder referred to by the `baseUrl` property in this file, not taking `extends` into account. */
35+
Folder getOwnBaseUrlFolder() {
36+
result = Resolver::resolve(this.getFolder(), this.getBaseUrlPath())
37+
}
38+
39+
/** Gets the effective baseUrl folder for this tsconfig file. */
40+
Folder getBaseUrlFolder() {
41+
result = this.getOwnBaseUrlFolder()
42+
or
43+
not exists(this.getOwnBaseUrlFolder()) and
44+
result = this.getExtendedTSConfig().getBaseUrlFolder()
45+
}
46+
47+
/** Gets the effective baseUrl folder for this tsconfig file, or its enclosing folder if there is no baseUrl. */
48+
Folder getBaseUrlFolderOrOwnFolder() {
49+
result = this.getBaseUrlFolder()
50+
or
51+
not exists(this.getBaseUrlFolder()) and
52+
result = this.getFolder()
53+
}
54+
55+
/** Gets a path mentioned in the `include` property. */
56+
string getAnIncludePath() {
57+
result = this.getPropStringValue("include")
58+
or
59+
result = this.getPropValue("include").(JsonArray).getElementStringValue(_)
60+
}
61+
62+
/**
63+
* Gets a file or folder refenced by a path the `include` property, possibly
64+
* inherited from an extended tsconfig file.
65+
*
66+
* Does not include all the files within includes directories, use `getAnIncludedContainer` for that.
67+
*/
68+
Container getAnIncludePathTarget() {
69+
result = Resolver::resolve(this.getFolder(), this.getAnIncludePath())
70+
or
71+
not exists(this.getPropValue("include")) and
72+
result = this.getExtendedTSConfig().getAnIncludePathTarget()
73+
}
74+
75+
/**
76+
* Gets a file or folder inside the directory tree mentioned in the `include` property.
77+
*/
78+
Container getAnIncludedContainer() {
79+
result = this.getAnIncludePathTarget()
80+
or
81+
result = this.getAnIncludedContainer().getAChildContainer()
82+
}
83+
84+
private JsonObject getPathMappings() { result = this.getCompilerOptions().getPropValue("paths") }
85+
86+
/**
87+
* Holds if this has a path mapping from `pattern` to `newPath`.
88+
*
89+
* For example, `"paths": { "@/*": "./src/*" }` maps the `@/*` pattern to `./src/*`.
90+
*
91+
* Does not include path mappings from extended tsconfig files.
92+
*/
93+
predicate hasPathMapping(string pattern, string newPath) {
94+
this.getPathMappings().getPropStringValue(pattern) = newPath
95+
or
96+
this.getPathMappings().getPropValue(pattern).(JsonArray).getElementStringValue(_) = newPath
97+
}
98+
99+
/**
100+
* Holds if this has an exact path mapping from `pattern` to `newPath`.
101+
*
102+
* For example, `"paths": { "@": "./src/index.ts" }` maps the `@` path to `./src/index.ts`.
103+
*
104+
* Does not include path mappings from extended tsconfig files.
105+
*/
106+
predicate hasExactPathMapping(string pattern, string newPath) {
107+
this.hasPathMapping(pattern, newPath) and
108+
not pattern.matches("%*%")
109+
}
110+
111+
/**
112+
* Holds if this has a path mapping from the `pattern` prefix to the `newPath` prefix.
113+
* The trailing `*` is not included.
114+
*
115+
* For example, `"paths": { "@/*": "./src/*" }` maps the `@/` pattern to `./src/`.
116+
*
117+
* Does not include path mappings from extended tsconfig files.
118+
*/
119+
predicate hasPrefixPathMapping(string pattern, string newPath) {
120+
this.hasPathMapping(pattern + "*", newPath + "*")
121+
}
122+
}
123+
124+
/** For resolving paths in a tsconfig file, except `paths` mappings. */
125+
private module ResolverConfig implements Folder::ResolveSig {
126+
predicate shouldResolve(Container base, string path) {
127+
exists(TSConfig cfg |
128+
base = cfg.getFolder() and
129+
path = [cfg.getExtendsPath(), cfg.getBaseUrlPath(), cfg.getAnIncludePath()]
130+
)
131+
}
132+
133+
predicate allowGlobs() { any() } // "include" can use globs
134+
}
135+
136+
private module Resolver = Folder::Resolve<ResolverConfig>;

0 commit comments

Comments
 (0)