Skip to content

Commit b565e3d

Browse files
committed
expand outDir support in tsconfig files
1 parent 3415b64 commit b565e3d

File tree

9 files changed

+98
-43
lines changed

9 files changed

+98
-43
lines changed

javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ private void setupFilters() {
407407
// codeql-javascript-*.json files
408408
patterns.add("**/.eslintrc*");
409409
patterns.add("**/package.json");
410-
patterns.add("**/tsconfig.json");
410+
patterns.add("**/tsconfig*.json");
411411
patterns.add("**/codeql-javascript-*.json");
412412

413413
// include any explicitly specified extensions

javascript/extractor/src/com/semmle/js/extractor/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class Main {
4343
* A version identifier that should be updated every time the extractor changes in such a way that
4444
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
4545
*/
46-
public static final String EXTRACTOR_VERSION = "2021-03-08";
46+
public static final String EXTRACTOR_VERSION = "2021-03-19";
4747

4848
public static final Pattern NEWLINE = Pattern.compile("\n");
4949

javascript/ql/src/semmle/javascript/Paths.qll

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -141,29 +141,7 @@ abstract class PathString extends string {
141141
* components of this path refers to when resolved relative to the
142142
* given `root` folder.
143143
*/
144-
Path resolveUpTo(int n, Folder root) {
145-
n = 0 and result.getContainer() = root and root = getARootFolder()
146-
or
147-
exists(Path base, string next | next = getComponent(this, n - 1, base, root) |
148-
// handle empty components and the special "." folder
149-
(next = "" or next = ".") and
150-
result = base
151-
or
152-
// handle the special ".." folder
153-
next = ".." and result = base.(ConsPath).getParent()
154-
or
155-
// special handling for Windows drive letters when resolving absolute path:
156-
// the extractor populates "C:/" as a folder that has path "C:/" but name ""
157-
n = 1 and
158-
next.regexpMatch("[A-Za-z]:") and
159-
root.getBaseName() = "" and
160-
root.toString() = next.toUpperCase() + "/" and
161-
result = base
162-
or
163-
// default case
164-
result = TConsPath(base, next)
165-
)
166-
}
144+
Path resolveUpTo(int n, Folder root) { result = resolveUpTo(this, n, root, _) }
167145

168146
/**
169147
* Gets the absolute path that this path refers to when resolved relative to
@@ -172,16 +150,57 @@ abstract class PathString extends string {
172150
Path resolve(Folder root) { result = resolveUpTo(getNumComponent(), root) }
173151
}
174152

153+
/**
154+
* Gets the absolute path that the sub-path consisting of the first `n`
155+
* components of this path refers to when resolved relative to the
156+
* given `root` folder.
157+
*/
158+
private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) {
159+
n = 0 and result.getContainer() = root and root = p.getARootFolder() and inTS = false
160+
or
161+
exists(Path base, string next | next = getComponent(p, n - 1, base, root, inTS) |
162+
// handle empty components and the special "." folder
163+
(next = "" or next = ".") and
164+
result = base
165+
or
166+
// handle the special ".." folder
167+
next = ".." and result = base.(ConsPath).getParent()
168+
or
169+
// special handling for Windows drive letters when resolving absolute path:
170+
// the extractor populates "C:/" as a folder that has path "C:/" but name ""
171+
n = 1 and
172+
next.regexpMatch("[A-Za-z]:") and
173+
root.getBaseName() = "" and
174+
root.toString() = next.toUpperCase() + "/" and
175+
result = base
176+
or
177+
// default case
178+
result = TConsPath(base, next)
179+
)
180+
}
181+
175182
/**
176183
* Gets the `i`th component of the path `str`, where `base` is the resolved path one level up.
177184
* Supports that the root directory might be compiled output from TypeScript.
185+
* `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`.
178186
*/
179-
private string getComponent(PathString str, int n, Path base, Folder root) {
180-
base = str.resolveUpTo(n, root) and
181-
(
182-
result = str.getComponent(n)
183-
or
184-
result = TypeScriptOutDir::getOriginalTypeScriptFolder(str.getComponent(n), base.getContainer())
187+
private string getComponent(PathString str, int n, Path base, Folder root, boolean inTS) {
188+
exists(boolean prevTS |
189+
base = resolveUpTo(str, n, root, prevTS) and
190+
(
191+
result = str.getComponent(n) and prevTS = inTS
192+
or
193+
// If we are in a TypeScript source folder, try to replace file endings with ".ts" or ".tsx"
194+
n = str.getNumComponent() - 1 and
195+
prevTS = true and
196+
inTS = prevTS and
197+
result = str.getComponent(n).regexpCapture("^(.*)\\.js$", 1) + "." + ["ts", "tsx"]
198+
or
199+
prevTS = false and
200+
inTS = true and
201+
result =
202+
TypeScriptOutDir::getOriginalTypeScriptFolder(str.getComponent(n), base.getContainer())
203+
)
185204
)
186205
}
187206

@@ -194,21 +213,35 @@ private module TypeScriptOutDir {
194213
*/
195214
string getOriginalTypeScriptFolder(string outdir, Folder parent) {
196215
exists(JSONObject tsconfig |
197-
tsconfig.getFile().getBaseName() = "tsconfig.json" and
198-
tsconfig.isTopLevel() and
199-
tsconfig.getFile().getParentContainer() = parent
200-
|
201-
outdir =
202-
tsconfig
203-
.getPropValue("compilerOptions")
204-
.(JSONObject)
205-
.getPropValue("outDir")
206-
.(JSONString)
207-
.getValue() and
208-
result = getEffectiveRootDirFromTSConfig(tsconfig)
216+
outdir = removeLeadingSlash(getOutDir(tsconfig, parent)) and
217+
result = removeLeadingSlash(getEffectiveRootDirFromTSConfig(tsconfig))
209218
)
210219
}
211220

221+
/**
222+
* Removes a leading dot and/or slash from `raw`.
223+
*/
224+
bindingset[raw]
225+
private string removeLeadingSlash(string raw) {
226+
result = raw.regexpCapture("^\\.?/?([\\w.\\-]+)$", 1)
227+
}
228+
229+
/**
230+
* Gets the `outDir` option from a tsconfig file from the folder `parent`.
231+
*/
232+
private string getOutDir(JSONObject tsconfig, Folder parent) {
233+
tsconfig.getFile().getBaseName().regexpMatch("tsconfig.*\\.json") and
234+
tsconfig.isTopLevel() and
235+
tsconfig.getFile().getParentContainer() = parent and
236+
result =
237+
tsconfig
238+
.getPropValue("compilerOptions")
239+
.(JSONObject)
240+
.getPropValue("outDir")
241+
.(JSONString)
242+
.getValue()
243+
}
244+
212245
/**
213246
* Gets the directory that contains the TypeScript source files.
214247
* Based on the tsconfig.json file `tsconfig`.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var foo1 = require('./lib/index.js');
2+
var foo2 = require('./src/index.ts');
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "foo",
3+
"main": "./lib/index.js"
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default class Foo {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig",
3+
"compilerOptions": {
4+
"module": "commonjs",
5+
"rootDir": "./src",
6+
"outDir": "./lib"
7+
}
8+
}

javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
resolveableImport
12
| nonUniqueInclude/index.js:1:12:1:38 | require ... oo.js') | nonUniqueInclude/src/foo.ts:1:1:1:27 | <toplevel> |
23
| nonUniqueInclude/index.js:2:12:2:39 | require ... oo.js') | nonUniqueInclude/src2/foo.ts:1:1:1:27 | <toplevel> |
34
| nonUniqueInclude/index.js:3:12:3:34 | require ... oo.ts') | nonUniqueInclude/src/foo.ts:1:1:1:27 | <toplevel> |
@@ -10,3 +11,7 @@
1011
| simpleOutDir/index.js:2:12:2:36 | require ... ex.ts') | simpleOutDir/src/index.ts:1:1:1:27 | <toplevel> |
1112
| simpleOutDir/index.js:4:12:4:33 | require ... index') | simpleOutDir/src/index.ts:1:1:1:27 | <toplevel> |
1213
| simpleOutDir/index.js:5:12:5:33 | require ... index') | simpleOutDir/src/index.ts:1:1:1:27 | <toplevel> |
14+
| slashAndExtension/index.js:1:12:1:36 | require ... ex.js') | slashAndExtension/src/index.ts:1:1:1:27 | <toplevel> |
15+
| slashAndExtension/index.js:2:12:2:36 | require ... ex.ts') | slashAndExtension/src/index.ts:1:1:1:27 | <toplevel> |
16+
getMain
17+
| slashAndExtension/package.json:1:1:4:1 | {\\n " ... x.js"\\n} | slashAndExtension/src/index.ts:1:1:1:27 | <toplevel> |

javascript/ql/test/library-tests/TypeScript/ImportOutDir/tests.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ query predicate resolveableImport(Import imp, Module mod) {
55
not imp.getTopLevel().isExterns() and
66
not mod.getTopLevel().isExterns()
77
}
8+
9+
query Module getMain(PackageJSON json) { result = json.getMainModule() }

0 commit comments

Comments
 (0)