Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 89a808c

Browse files
authored
Merge pull request #4552 from erik-krogh/tsImport
Approved by asgerf
2 parents b55f18b + 39028f6 commit 89a808c

18 files changed

Lines changed: 193 additions & 20 deletions

File tree

javascript/ql/src/semmle/javascript/NodeJS.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ class Require extends CallExpr, Import {
235235

236236
override Module resolveImportedPath() {
237237
moduleInFile(result, load(min(int prio | moduleInFile(_, load(prio)))))
238+
or
239+
not exists(Module mod | moduleInFile(mod, load(_))) and
240+
result = Import.super.resolveImportedPath()
238241
}
239242

240243
/**

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

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,24 @@ abstract class PathString extends string {
144144
Path resolveUpTo(int n, Folder root) {
145145
n = 0 and result.getContainer() = root and root = getARootFolder()
146146
or
147-
exists(Path base | base = resolveUpTo(n - 1, root) |
148-
exists(string next | next = getComponent(n - 1) |
149-
// handle empty components and the special "." folder
150-
(next = "" or next = ".") and
151-
result = base
152-
or
153-
// handle the special ".." folder
154-
next = ".." and result = base.(ConsPath).getParent()
155-
or
156-
// special handling for Windows drive letters when resolving absolute path:
157-
// the extractor populates "C:/" as a folder that has path "C:/" but name ""
158-
n = 1 and
159-
next.regexpMatch("[A-Za-z]:") and
160-
root.getBaseName() = "" and
161-
root.toString() = next.toUpperCase() + "/" and
162-
result = base
163-
or
164-
// default case
165-
result = TConsPath(base, next)
166-
)
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)
167165
)
168166
}
169167

@@ -174,6 +172,105 @@ abstract class PathString extends string {
174172
Path resolve(Folder root) { result = resolveUpTo(getNumComponent(), root) }
175173
}
176174

175+
/**
176+
* Gets the `i`th component of the path `str`, where `base` is the resolved path one level up.
177+
* Supports that the root directory might be compiled output from TypeScript.
178+
*/
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())
185+
)
186+
}
187+
188+
/**
189+
* Predicates for resolving imports to compiled TypeScript.
190+
*/
191+
private module TypeScriptOutDir {
192+
/**
193+
* Gets a folder of TypeScript files that is compiled to JavaScript files in `outdir` relative to a `parent`.
194+
*/
195+
string getOriginalTypeScriptFolder(string outdir, Folder parent) {
196+
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)
209+
)
210+
}
211+
212+
/**
213+
* Gets the directory that contains the TypeScript source files.
214+
* Based on the tsconfig.json file `tsconfig`.
215+
*/
216+
pragma[inline]
217+
private string getEffectiveRootDirFromTSConfig(JSONObject tsconfig) {
218+
// if an explicit "rootDir" option exists, then use that.
219+
result = getRootDir(tsconfig)
220+
or
221+
// otherwise, infer from "includes"
222+
not exists(getRootDir(tsconfig)) and
223+
(
224+
// if unique root folder in "includes", then use that.
225+
result = unique( | | getARootDirFromInclude(tsconfig))
226+
or
227+
// otherwise use "." if the includes are split over multiple folders.
228+
exists(getARootDirFromInclude(tsconfig)) and
229+
not exists(unique( | | getARootDirFromInclude(tsconfig))) and
230+
result = "."
231+
)
232+
}
233+
234+
/**
235+
* Gets the first folder from `path`.
236+
*/
237+
bindingset[path]
238+
private string getRootFolderFromPath(string path) {
239+
not exists(path.indexOf("/")) and result = path
240+
or
241+
result = path.substring(0, path.indexOf("/", 0, 0))
242+
}
243+
244+
/**
245+
* Gets a root directory containing TypeScript files based on the "include" option from tsconfig.json.
246+
* Can have multiple results if the includes are from multiple folders.
247+
*/
248+
pragma[inline]
249+
private string getARootDirFromInclude(JSONObject tsconfig) {
250+
result =
251+
getRootFolderFromPath(tsconfig
252+
.getPropValue("include")
253+
.(JSONArray)
254+
.getElementValue(_)
255+
.(JSONString)
256+
.getValue())
257+
}
258+
259+
/**
260+
* Gets the value of the "rootDir" option from a tsconfig.json.
261+
*/
262+
pragma[inline]
263+
private string getRootDir(JSONObject tsconfig) {
264+
result =
265+
tsconfig
266+
.getPropValue("compilerOptions")
267+
.(JSONObject)
268+
.getPropValue("rootDir")
269+
.(JSONString)
270+
.getValue()
271+
}
272+
}
273+
177274
/**
178275
* An expression whose value represents a (relative or absolute) file system path.
179276
*
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var foo1 = require('./lib/src/foo.js');
2+
var foo1 = require('./lib/src2/foo.js');
3+
var foo2 = require('./src/foo.ts');
4+
var foo2 = require('./src2/foo.ts');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default class Foo {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default class Foo {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"outDir": "lib"
4+
},
5+
"include": [
6+
"src/**/*.ts",
7+
"src2/**/*.ts"
8+
]
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
semmle-extractor-options: --include **/tsconfig.json
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default class Foo {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"outDir": "lib",
4+
"rootDir": "src"
5+
},
6+
"include": [
7+
"**/*.ts"
8+
]
9+
}

0 commit comments

Comments
 (0)