Skip to content

Commit 6206258

Browse files
committed
JS: Update implementation
1 parent 51813c9 commit 6206258

File tree

5 files changed

+333
-215
lines changed

5 files changed

+333
-215
lines changed

javascript/ql/lib/semmle/javascript/internal/paths/JSPaths.qll

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,11 @@ module JSPaths {
4343
//
4444
// { include: ["foo"], compilerOptions: { outDir: "./bar" }}
4545
//
46-
exists(TSConfig tsconfig, Container includeTarget |
46+
exists(TSConfig tsconfig |
4747
name =
4848
tsconfig.getCompilerOptions().getPropStringValue("outDir").regexpReplaceAll("^\\./", "") and
4949
base = tsconfig.getFolder() and
50-
includeTarget = tsconfig.getAnIncludePathTarget()
51-
|
52-
result = includeTarget.(Folder)
53-
or
54-
result = includeTarget.(File).getParentContainer()
50+
result = tsconfig.getEffectiveRootDir()
5551
)
5652
or
5753
// Heuristic version of the above based on commonly used source and build folder names

javascript/ql/lib/semmle/javascript/internal/paths/PackageJsonEx.qll

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,34 @@ class PackageJsonEx extends PackageJson {
5252
)
5353
}
5454

55+
File getMainFileOrBestGuess() {
56+
result = this.getMainFile()
57+
or
58+
result = guessPackageJsonMain1(this)
59+
or
60+
result = guessPackageJsonMain2(this)
61+
}
62+
5563
string getAPathInFilesArray() {
5664
result = this.getPropValue("files").(JsonArray).getElementStringValue(_)
5765
}
5866

5967
Container getAFileInFilesArray() {
6068
result = Resolver::resolve(this.getFolder(), this.getAPathInFilesArray())
6169
}
70+
71+
override File getTypingsFile() {
72+
result = Resolver::resolve(this.getFolder(), this.getTypings())
73+
or
74+
not exists(this.getTypings()) and
75+
exists(File mainFile |
76+
mainFile = this.getMainFileOrBestGuess() and
77+
result =
78+
mainFile
79+
.getParentContainer()
80+
.getFile(mainFile.getStem().regexpReplaceAll("\\.d$", "") + ".d.ts")
81+
)
82+
}
6283
}
6384

6485
private module ResolverConfig implements Folder::ResolveSig {
@@ -72,6 +93,8 @@ private module ResolverConfig implements Folder::ResolveSig {
7293
path = pkg.getMainPath()
7394
or
7495
path = pkg.getAPathInFilesArray()
96+
or
97+
path = pkg.getTypings()
7598
)
7699
}
77100

@@ -80,6 +103,43 @@ private module ResolverConfig implements Folder::ResolveSig {
80103
predicate getAnAdditionalChild = JSPaths::getAnAdditionalChild/2;
81104

82105
predicate isOptionalPathComponent(string segment) { segment = ["cjs", "mjs", "js"] }
106+
107+
bindingset[segment]
108+
string rewritePathSegment(string segment) { result = segment.regexpReplaceAll("\\..*", "") }
83109
}
84110

85111
private module Resolver = Folder::Resolve<ResolverConfig>;
112+
113+
/**
114+
* Removes the scope from a package name, e.g. `@foo/bar` -> `bar`.
115+
*/
116+
bindingset[name]
117+
private string stripPackageScope(string name) { result = name.regexpReplaceAll("^@[^/]+/", "") }
118+
119+
private predicate isImplementationFile(File f) { not f.getBaseName().matches("%.d.ts") }
120+
121+
File guessPackageJsonMain1(PackageJsonEx pkg) {
122+
not isImplementationFile(pkg.getMainFile()) and
123+
exists(Folder folder, Folder subfolder |
124+
folder = pkg.getFolder() and
125+
(
126+
subfolder = folder or
127+
subfolder = folder.getChildContainer(getASrcFolderName()) or
128+
subfolder =
129+
folder
130+
.getChildContainer(getASrcFolderName())
131+
.(Folder)
132+
.getChildContainer(getASrcFolderName())
133+
)
134+
|
135+
result = subfolder.getJavaScriptFileOrTypings("index")
136+
or
137+
result = subfolder.getJavaScriptFileOrTypings(stripPackageScope(pkg.getDeclaredPackageName()))
138+
)
139+
}
140+
141+
File guessPackageJsonMain2(PackageJsonEx pkg) {
142+
not isImplementationFile(pkg.getMainFile()) and
143+
not isImplementationFile(guessPackageJsonMain1(pkg)) and
144+
result = pkg.getAFileInFilesArray()
145+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
private import javascript
2+
3+
/**
4+
* A path expression that can be constant-folded by concatenating subexpressions.
5+
*/
6+
abstract class PathConcatenation extends Expr {
7+
/** Gets the separator to insert between paths */
8+
string getSeparator() { result = "" }
9+
10+
/** Gets the `n`th operand to concatenate. */
11+
abstract Expr getOperand(int n);
12+
}
13+
14+
private class AddExprConcatenation extends PathConcatenation, AddExpr {
15+
override Expr getOperand(int n) {
16+
n = 0 and result = this.getLeftOperand()
17+
or
18+
n = 1 and result = this.getRightOperand()
19+
}
20+
}
21+
22+
private class TemplateConcatenation extends PathConcatenation, TemplateLiteral {
23+
override Expr getOperand(int n) { result = this.getElement(n) }
24+
}
25+
26+
private class JoinCallConcatenation extends PathConcatenation, CallExpr {
27+
JoinCallConcatenation() {
28+
this.getReceiver().(VarAccess).getName() = "path" and
29+
this.getCalleeName() = ["join", "resolve"]
30+
}
31+
32+
override Expr getOperand(int n) { result = this.getArgument(n) }
33+
34+
override string getSeparator() { result = "/" }
35+
}

0 commit comments

Comments
 (0)