Merge pull request #19391 from asgerf/js/typescript-path-resolution

JS: Overhaul import resolution
This commit is contained in:
Asger F
2025-05-13 15:46:38 +02:00
committed by GitHub
159 changed files with 1855 additions and 471 deletions

View File

@@ -404,7 +404,7 @@ public class AutoBuild {
patterns.add("**/*.view.json"); // SAP UI5
patterns.add("**/manifest.json");
patterns.add("**/package.json");
patterns.add("**/tsconfig*.json");
patterns.add("**/*tsconfig*.json");
patterns.add("**/codeql-javascript-*.json");
// include any explicitly specified extensions

View File

@@ -29,7 +29,7 @@ public class JSONExtractor implements IExtractor {
private final boolean tolerateParseErrors;
public JSONExtractor(ExtractorConfig config) {
this.tolerateParseErrors = config.isTolerateParseErrors();
this.tolerateParseErrors = true;
}
@Override

View File

@@ -301,9 +301,14 @@ public class Main {
// only extract HTML and JS by default
addIncludesFor(includes, FileType.HTML);
addIncludesFor(includes, FileType.JS);
includes.add("**/.babelrc*.json");
// extract TypeScript if `--typescript` or `--typescript-full` was specified
if (getTypeScriptMode(ap) != TypeScriptMode.NONE) addIncludesFor(includes, FileType.TYPESCRIPT);
if (getTypeScriptMode(ap) != TypeScriptMode.NONE) {
addIncludesFor(includes, FileType.TYPESCRIPT);
includes.add("**/*tsconfig*.json");
}
// add explicit include patterns
for (String pattern : ap.getZeroOrMore(P_INCLUDE))

View File

@@ -11,5 +11,5 @@
import javascript
from ImportDeclaration id
where id.getImportedPath().getValue() = "react"
where id.getImportedPathString() = "react"
select id

View File

@@ -70,7 +70,7 @@ private predicate importLookup(AstNode path, Module target, string kind) {
kind = "I" and
(
exists(Import i |
path = i.getImportedPath() and
path = i.getImportedPathExpr() and
target = i.getImportedModule()
)
or

View File

@@ -61,8 +61,14 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
result = this.getArgument(1)
}
/** DEPRECATED. Use `getDependencyExpr` instead. */
deprecated PathExpr getDependency(int i) { result = this.getDependencyExpr(i) }
/** DEPRECATED. Use `getADependencyExpr` instead. */
deprecated PathExpr getADependency() { result = this.getADependencyExpr() }
/** Gets the `i`th dependency of this module definition. */
PathExpr getDependency(int i) {
Expr getDependencyExpr(int i) {
exists(Expr expr |
expr = this.getDependencies().getElement(i) and
not isPseudoDependency(expr.getStringValue()) and
@@ -71,8 +77,8 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
}
/** Gets a dependency of this module definition. */
PathExpr getADependency() {
result = this.getDependency(_) or
Expr getADependencyExpr() {
result = this.getDependencyExpr(_) or
result = this.getARequireCall().getAnArgument()
}
@@ -233,7 +239,7 @@ private class AmdDependencyPath extends PathExprCandidate {
}
/** A constant path element appearing in an AMD dependency expression. */
private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString {
deprecated private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString {
ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() }
override string getValue() { result = this.getStringValue() }
@@ -261,11 +267,13 @@ private predicate amdModuleTopLevel(AmdModuleDefinition def, TopLevel tl) {
* An AMD dependency, viewed as an import.
*/
private class AmdDependencyImport extends Import {
AmdDependencyImport() { this = any(AmdModuleDefinition def).getADependency() }
AmdDependencyImport() { this = any(AmdModuleDefinition def).getADependencyExpr() }
override Module getEnclosingModule() { this = result.(AmdModule).getDefine().getADependency() }
override Module getEnclosingModule() {
this = result.(AmdModule).getDefine().getADependencyExpr()
}
override PathExpr getImportedPath() { result = this }
override Expr getImportedPathExpr() { result = this }
/**
* Gets a file that looks like it might be the target of this import.
@@ -274,7 +282,7 @@ private class AmdDependencyImport extends Import {
* adding well-known JavaScript file extensions like `.js`.
*/
private File guessTarget() {
exists(PathString imported, string abspath, string dirname, string basename |
exists(FilePath imported, string abspath, string dirname, string basename |
this.targetCandidate(result, abspath, imported, dirname, basename)
|
abspath.regexpMatch(".*/\\Q" + imported + "\\E")
@@ -296,9 +304,9 @@ private class AmdDependencyImport extends Import {
* `dirname` and `basename` to the dirname and basename (respectively) of `imported`.
*/
private predicate targetCandidate(
File f, string abspath, PathString imported, string dirname, string basename
File f, string abspath, FilePath imported, string dirname, string basename
) {
imported = this.getImportedPath().getValue() and
imported = this.getImportedPathString() and
f.getStem() = imported.getStem() and
f.getAbsolutePath() = abspath and
dirname = imported.getDirName() and

View File

@@ -2,6 +2,7 @@
import javascript
private import semmle.javascript.internal.CachedStages
private import semmle.javascript.internal.paths.PathExprResolver
/**
* An ECMAScript 2015 module.
@@ -91,7 +92,12 @@ private predicate hasDefaultExport(ES2015Module mod) {
class ImportDeclaration extends Stmt, Import, @import_declaration {
override ES2015Module getEnclosingModule() { result = this.getTopLevel() }
override PathExpr getImportedPath() { result = this.getChildExpr(-1) }
/**
* INTERNAL USE ONLY. DO NOT USE.
*/
string getRawImportPath() { result = this.getChildExpr(-1).getStringValue() }
override Expr getImportedPathExpr() { result = this.getChildExpr(-1) }
/**
* Gets the object literal passed as part of the `with` (or `assert`) clause in this import declaration.
@@ -149,7 +155,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
}
/** A literal path expression appearing in an `import` declaration. */
private class LiteralImportPath extends PathExpr, ConstantString {
deprecated private class LiteralImportPath extends PathExpr, ConstantString {
LiteralImportPath() { exists(ImportDeclaration req | this = req.getChildExpr(-1)) }
override string getValue() { result = this.getStringValue() }
@@ -725,27 +731,12 @@ abstract class ReExportDeclaration extends ExportDeclaration {
cached
Module getReExportedModule() {
Stages::Imports::ref() and
result.getFile() = this.getEnclosingModule().resolve(this.getImportedPath())
or
result = this.resolveFromTypeRoot()
}
/**
* Gets a module in a `node_modules/@types/` folder that matches the imported module name.
*/
private Module resolveFromTypeRoot() {
result.getFile() =
min(TypeRootFolder typeRoot |
|
typeRoot.getModuleFile(this.getImportedPath().getStringValue())
order by
typeRoot.getSearchPriority(this.getFile().getParentContainer())
)
result.getFile() = ImportPathResolver::resolveExpr(this.getImportedPath())
}
}
/** A literal path expression appearing in a re-export declaration. */
private class LiteralReExportPath extends PathExpr, ConstantString {
deprecated private class LiteralReExportPath extends PathExpr, ConstantString {
LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) }
override string getValue() { result = this.getStringValue() }

View File

@@ -2821,7 +2821,7 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import {
result = this.getSource().getFirstControlFlowNode()
}
override PathExpr getImportedPath() { result = this.getSource() }
override Expr getImportedPathExpr() { result = this.getSource() }
/**
* Gets the second "argument" to the import expression, that is, the `Y` in `import(X, Y)`.
@@ -2852,7 +2852,7 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import {
}
/** A literal path expression appearing in a dynamic import. */
private class LiteralDynamicImportPath extends PathExpr, ConstantString {
deprecated private class LiteralDynamicImportPath extends PathExpr, ConstantString {
LiteralDynamicImportPath() {
exists(DynamicImportExpr di | this.getParentExpr*() = di.getSource())
}

View File

@@ -29,6 +29,8 @@ private module Impl = Make<FsInput>;
class Container = Impl::Container;
module Folder = Impl::Folder;
/** A folder. */
class Folder extends Container, Impl::Folder {
/** Gets the file or subfolder in this folder that has the given `name`, if any. */
@@ -73,6 +75,19 @@ class Folder extends Container, Impl::Folder {
)
}
/**
* Gets an implementation file and/or a typings file from this folder that has the given `stem`.
* This could be a single `.ts` file or a pair of `.js` and `.d.ts` files.
*/
File getJavaScriptFileOrTypings(string stem) {
exists(File jsFile | jsFile = this.getJavaScriptFile(stem) |
result = jsFile
or
not jsFile.getFileType().isTypeScript() and
result = this.getFile(stem + ".d.ts")
)
}
/** Gets a subfolder contained in this folder. */
Folder getASubFolder() { result = this.getAChildContainer() }
}

View File

@@ -214,7 +214,7 @@ module HTML {
result = path.regexpCapture("file://(/.*)", 1)
or
not path.regexpMatch("(\\w+:)?//.*") and
result = this.getSourcePath().(ScriptSrcPath).resolve(this.getSearchRoot()).toString()
result = ResolveScriptSrc::resolve(this.getSearchRoot(), this.getSourcePath()).toString()
)
}
@@ -274,10 +274,16 @@ module HTML {
)
}
private module ResolverConfig implements Folder::ResolveSig {
predicate shouldResolve(Container base, string path) { scriptSrc(path, base) }
}
private module ResolveScriptSrc = Folder::Resolve<ResolverConfig>;
/**
* A path string arising from the `src` attribute of a `script` tag.
*/
private class ScriptSrcPath extends PathString {
deprecated private class ScriptSrcPath extends PathString {
ScriptSrcPath() { scriptSrc(this, _) }
override Folder getARootFolder() { scriptSrc(this, result) }

View File

@@ -6,6 +6,7 @@
import javascript
private import semmle.javascript.internal.CachedStages
private import semmle.javascript.internal.paths.PathExprResolver
/**
* A module, which may either be an ECMAScript 2015-style module,
@@ -68,7 +69,7 @@ abstract class Module extends TopLevel {
* This predicate is not part of the public API, it is only exposed to allow
* overriding by subclasses.
*/
predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
deprecated predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
path.getEnclosingModule() = this and
priority = 0 and
exists(string v | v = path.getValue() |
@@ -89,7 +90,7 @@ abstract class Module extends TopLevel {
* resolves to a folder containing a main module (such as `index.js`), then
* that file is the result.
*/
File resolve(PathExpr path) {
deprecated File resolve(PathExpr path) {
path.getEnclosingModule() = this and
(
// handle the case where the import path is complete
@@ -122,8 +123,14 @@ abstract class Import extends AstNode {
/** Gets the module in which this import appears. */
abstract Module getEnclosingModule();
/** DEPRECATED. Use `getImportedPathExpr` instead. */
deprecated PathExpr getImportedPath() { result = this.getImportedPathExpr() }
/** Gets the (unresolved) path that this import refers to. */
abstract PathExpr getImportedPath();
abstract Expr getImportedPathExpr();
/** Gets the imported path as a string. */
final string getImportedPathString() { result = this.getImportedPathExpr().getStringValue() }
/**
* Gets an externs module the path of this import resolves to.
@@ -132,45 +139,23 @@ abstract class Import extends AstNode {
* path is assumed to be a possible target of the import.
*/
Module resolveExternsImport() {
result.isExterns() and result.getName() = this.getImportedPath().getValue()
result.isExterns() and result.getName() = this.getImportedPathString()
}
/**
* Gets the module the path of this import resolves to.
*/
Module resolveImportedPath() {
result.getFile() = this.getEnclosingModule().resolve(this.getImportedPath())
}
Module resolveImportedPath() { result.getFile() = this.getImportedFile() }
/**
* Gets a module with a `@providesModule` JSDoc tag that matches
* the imported path.
* Gets the module the path of this import resolves to.
*/
private Module resolveAsProvidedModule() {
exists(JSDocTag tag |
tag.getTitle() = "providesModule" and
tag.getParent().getComment().getTopLevel() = result and
tag.getDescription().trim() = this.getImportedPath().getValue()
)
}
File getImportedFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) }
/**
* Gets a module in a `node_modules/@types/` folder that matches the imported module name.
* DEPRECATED. Use `getImportedModule()` instead.
*/
private Module resolveFromTypeRoot() {
result.getFile() =
min(TypeRootFolder typeRoot |
|
typeRoot.getModuleFile(this.getImportedPath().getValue())
order by
typeRoot.getSearchPriority(this.getFile().getParentContainer())
)
}
/**
* Gets the imported module, as determined by the TypeScript compiler, if any.
*/
private Module resolveFromTypeScriptSymbol() {
deprecated Module resolveFromTypeScriptSymbol() {
exists(CanonicalName symbol |
ast_node_symbol(this, symbol) and
ast_node_symbol(result, symbol)
@@ -190,13 +175,7 @@ abstract class Import extends AstNode {
Stages::Imports::ref() and
if exists(this.resolveExternsImport())
then result = this.resolveExternsImport()
else (
result = this.resolveAsProvidedModule() or
result = this.resolveImportedPath() or
result = this.resolveFromTypeRoot() or
result = this.resolveFromTypeScriptSymbol() or
result = resolveNeighbourPackage(this.getImportedPath().getValue())
)
else result = this.resolveImportedPath()
}
/**
@@ -204,28 +183,3 @@ abstract class Import extends AstNode {
*/
abstract DataFlow::Node getImportedModuleNode();
}
/**
* Gets a module imported from another package in the same repository.
*
* No support for importing from folders inside the other package.
*/
private Module resolveNeighbourPackage(PathString importPath) {
exists(PackageJson json | importPath = json.getPackageName() and result = json.getMainModule())
or
exists(string package |
result.getFile().getParentContainer() = getPackageFolder(package) and
importPath = package + "/" + [result.getFile().getBaseName(), result.getFile().getStem()]
)
}
/**
* Gets the folder for a package that has name `package` according to a package.json file in the resulting folder.
*/
pragma[noinline]
private Folder getPackageFolder(string package) {
exists(PackageJson json |
json.getPackageName() = package and
result = json.getFile().getParentContainer()
)
}

View File

@@ -4,6 +4,7 @@
import javascript
private import NodeModuleResolutionImpl
private import semmle.javascript.internal.paths.PackageJsonEx
/** A `package.json` configuration object. */
class PackageJson extends JsonObject {
@@ -12,12 +13,21 @@ class PackageJson extends JsonObject {
this.isTopLevel()
}
/** Gets the folder containing this `package.json` file. */
Folder getFolder() { result = this.getJsonFile().getParentContainer() }
/**
* Gets the name of this package as it appears in the `name` field.
*/
pragma[nomagic]
string getDeclaredPackageName() { result = this.getPropStringValue("name") }
/**
* Gets the name of this package.
* If the package is located under the package `pkg1` and its relative path is `foo/bar`, then the resulting package name will be `pkg1/foo/bar`.
*/
string getPackageName() {
result = this.getPropStringValue("name")
result = this.getDeclaredPackageName()
or
exists(
PackageJson parentPkg, Container currentDir, Container parentDir, string parentPkgName,
@@ -84,7 +94,10 @@ class PackageJson extends JsonObject {
* `module` paths to be exported under the relative path `"."`.
*/
string getExportedPath(string relativePath) {
result = MainModulePath::of(this, relativePath).getValue()
this.(PackageJsonEx).hasExactPathMapping(relativePath, result)
or
relativePath = "." and
result = this.(PackageJsonEx).getMainPath()
}
/** Gets the path of a command defined for this package. */
@@ -211,7 +224,7 @@ class PackageJson extends JsonObject {
/**
* Gets the main module of this package.
*/
Module getMainModule() { result = this.getExportedModule(".") }
Module getMainModule() { result.getFile() = this.(PackageJsonEx).getMainFileOrBestGuess() }
/**
* Gets the module exported under the given relative path.
@@ -219,12 +232,10 @@ class PackageJson extends JsonObject {
* The main module is considered exported under the path `"."`.
*/
Module getExportedModule(string relativePath) {
result =
min(Module m, int prio |
m.getFile() = resolveMainModule(this, prio, relativePath)
|
m order by prio
)
this.(PackageJsonEx).hasExactPathMappingTo(relativePath, result.getFile())
or
relativePath = "." and
result = this.getMainModule()
}
/**
@@ -236,19 +247,7 @@ class PackageJson extends JsonObject {
* Gets the file containing the typings of this package, which can either be from the `types` or
* `typings` field, or derived from the `main` or `module` fields.
*/
File getTypingsFile() {
result =
TypingsModulePathString::of(this).resolve(this.getFile().getParentContainer()).getContainer()
or
not exists(TypingsModulePathString::of(this)) and
exists(File mainFile |
mainFile = this.getMainModule().getFile() and
result =
mainFile
.getParentContainer()
.getFile(mainFile.getStem().regexpReplaceAll("\\.d$", "") + ".d.ts")
)
}
File getTypingsFile() { none() } // implemented in PackageJsonEx
/**
* Gets the module containing the typings of this package, which can either be from the `types` or

View File

@@ -146,7 +146,7 @@ class NodeModule extends Module {
)
}
override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
deprecated override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
path.getEnclosingModule() = this and
exists(string pathval | pathval = path.getValue() |
// paths starting with `./` or `../` are resolved relative to the importing
@@ -236,13 +236,8 @@ private class RequireVariable extends Variable {
}
}
/**
* Holds if module `m` is in file `f`.
*/
private predicate moduleInFile(Module m, File f) { m.getFile() = f }
private predicate isModuleModule(EarlyStageNode nd) {
exists(ImportDeclaration imp | imp.getImportedPath().getValue() = "module" |
exists(ImportDeclaration imp | imp.getRawImportPath() = "module" |
nd = TDestructuredModuleImportNode(imp)
or
nd = TValueNode(imp.getASpecifier().(ImportNamespaceSpecifier))
@@ -268,7 +263,7 @@ private predicate isCreateRequire(EarlyStageNode nd) {
)
or
exists(ImportDeclaration decl, NamedImportSpecifier spec |
decl.getImportedPath().getValue() = "module" and
decl.getRawImportPath() = "module" and
spec = decl.getASpecifier() and
spec.getImportedName() = "createRequire" and
nd = TValueNode(spec)
@@ -328,94 +323,15 @@ private predicate isRequire(EarlyStageNode nd) {
class Require extends CallExpr, Import {
Require() { isRequire(TValueNode(this.getCallee())) }
override PathExpr getImportedPath() { result = this.getArgument(0) }
override Expr getImportedPathExpr() { result = this.getArgument(0) }
override Module getEnclosingModule() { this = result.getAnImport() }
override Module resolveImportedPath() {
moduleInFile(result, this.load(min(int prio | moduleInFile(_, this.load(prio)))))
or
not moduleInFile(_, this.load(_)) and
result = Import.super.resolveImportedPath()
}
/**
* Gets the file that is imported by this `require`.
*
* The result can be a JavaScript file, a JSON file or a `.node` file.
* Externs files are not treated differently from other files by this predicate.
*/
File getImportedFile() { result = this.load(min(int prio | exists(this.load(prio)))) }
/**
* Gets the file that this `require` refers to (which may not be a JavaScript file),
* using the root folder of priority `priority`.
*
* This predicate implements the specification of
* [`require.resolve`](https://nodejs.org/api/modules.html#modules_all_together),
* modified to allow additional JavaScript file extensions, such as `ts` and `jsx`.
*
* Module resolution order is modeled using the `priority` parameter as follows.
*
* Each candidate folder in which the path may be resolved is assigned
* a priority (this is actually done by `Module.searchRoot`, but we explain it
* here for completeness):
*
* - if the path starts with `'./'`, `'../'`, or `/`, it has a single candidate
* folder (the enclosing folder of the module for the former two, the file
* system root for the latter) of priority 0
* - otherwise, candidate folders are folders of the form `<prefix>/node_modules`
* such that `<prefix>` is a (not necessarily proper) ancestor of the enclosing
* folder of the module which is not itself named `node_modules`; the priority
* of a candidate folder is the number of steps from the enclosing folder of
* the module to `<prefix>`.
*
* To resolve an import of a path `p`, we consider each candidate folder `c` with
* priority `r` and resolve the import to the following files if they exist
* (in order of priority):
*
* <ul>
* <li> the file `c/p`;
* <li> the file `c/p.{tsx,ts,jsx,es6,es,mjs,cjs}`;
* <li> the file `c/p.js`;
* <li> the file `c/p.json`;
* <li> the file `c/p.node`;
* <li> if `c/p` is a folder:
* <ul>
* <li> if `c/p/package.json` exists and specifies a `main` module `m`:
* <ul>
* <li> the file `c/p/m`;
* <li> the file `c/p/m.{tsx,ts,jsx,es6,es,mjs,cjs}`;
* <li> the file `c/p/m.js`;
* <li> the file `c/p/m.json`;
* <li> the file `c/p/m.node`;
* </ul>
* <li> the file `c/p/index.{tsx,ts,jsx,es6,es,mjs,cjs}`;
* <li> the file `c/p/index.js`;
* <li> the file `c/p/index.json`;
* <li> the file `c/p/index.node`.
* </ul>
* </ul>
*
* The first four steps are factored out into predicate `loadAsFile`,
* the remainder into `loadAsDirectory`; both make use of an auxiliary
* predicate `tryExtensions` that handles the repeated distinction between
* `.js`, `.json` and `.node`.
*/
private File load(int priority) {
exists(int r | this.getEnclosingModule().searchRoot(this.getImportedPath(), _, r) |
result = loadAsFile(this, r, priority - prioritiesPerCandidate() * r) or
result =
loadAsDirectory(this, r,
priority - (prioritiesPerCandidate() * r + numberOfExtensions() + 1))
)
}
override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) }
}
/** An argument to `require` or `require.resolve`, considered as a path expression. */
private class RequirePath extends PathExprCandidate {
deprecated private class RequirePath extends PathExprCandidate {
RequirePath() {
this = any(Require req).getArgument(0)
or
@@ -428,14 +344,14 @@ private class RequirePath extends PathExprCandidate {
}
/** A constant path element appearing in a call to `require` or `require.resolve`. */
private class ConstantRequirePathElement extends PathExpr, ConstantString {
deprecated private class ConstantRequirePathElement extends PathExpr, ConstantString {
ConstantRequirePathElement() { this = any(RequirePath rp).getAPart() }
override string getValue() { result = this.getStringValue() }
}
/** A `__dirname` path expression. */
private class DirNamePath extends PathExpr, VarAccess {
deprecated private class DirNamePath extends PathExpr, VarAccess {
DirNamePath() {
this.getName() = "__dirname" and
this.getVariable().getScope() instanceof ModuleScope
@@ -445,7 +361,7 @@ private class DirNamePath extends PathExpr, VarAccess {
}
/** A `__filename` path expression. */
private class FileNamePath extends PathExpr, VarAccess {
deprecated private class FileNamePath extends PathExpr, VarAccess {
FileNamePath() {
this.getName() = "__filename" and
this.getVariable().getScope() instanceof ModuleScope
@@ -458,7 +374,7 @@ private class FileNamePath extends PathExpr, VarAccess {
* A path expression of the form `path.join(p, "...")` where
* `p` is also a path expression.
*/
private class JoinedPath extends PathExpr, @call_expr {
deprecated private class JoinedPath extends PathExpr, @call_expr {
JoinedPath() {
exists(MethodCallExpr call | call = this |
call.getReceiver().(VarAccess).getName() = "path" and

View File

@@ -45,7 +45,7 @@ int numberOfExtensions() { result = count(getFileExtensionPriority(_)) }
* Gets the resolution target with the given `priority` of `req`
* when resolved from the root with priority `rootPriority`.
*/
File loadAsFile(Require req, int rootPriority, int priority) {
deprecated File loadAsFile(Require req, int rootPriority, int priority) {
exists(PathExpr path | path = req.getImportedPath() |
result = path.resolve(rootPriority) and priority = 0
or
@@ -60,7 +60,7 @@ File loadAsFile(Require req, int rootPriority, int priority) {
* with the given `priority` of `req` when resolved from the root with
* priority `rootPriority`.
*/
File loadAsDirectory(Require req, int rootPriority, int priority) {
deprecated File loadAsDirectory(Require req, int rootPriority, int priority) {
exists(Folder dir | dir = req.getImportedPath().resolve(rootPriority) |
result = resolveMainModule(dir.(NpmPackage).getPackageJson(), priority, ".") or
result = tryExtensions(dir, "index", priority - (numberOfExtensions() + 1))
@@ -99,7 +99,7 @@ private string getStem(string name) {
* Gets a file that a main module from `pkg` exported as `mainPath` with the given `priority`.
* `mainPath` is "." if it's the main module of the package.
*/
private File resolveMainPath(PackageJson pkg, string mainPath, int priority) {
deprecated private File resolveMainPath(PackageJson pkg, string mainPath, int priority) {
exists(PathExpr main | main = MainModulePath::of(pkg, mainPath) |
result = main.resolve() and priority = 0
or
@@ -132,7 +132,7 @@ private File resolveMainPath(PackageJson pkg, string mainPath, int priority) {
/**
* Gets the main module described by `pkg` with the given `priority`.
*/
File resolveMainModule(PackageJson pkg, int priority, string exportPath) {
deprecated File resolveMainModule(PackageJson pkg, int priority, string exportPath) {
result = resolveMainPath(pkg, exportPath, priority)
or
exportPath = "." and
@@ -178,7 +178,7 @@ private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] }
* A JSON string in a `package.json` file specifying the path of one of the exported
* modules of the package.
*/
class MainModulePath extends PathExpr, @json_string {
deprecated class MainModulePath extends PathExpr, @json_string {
PackageJson pkg;
MainModulePath() {
@@ -228,7 +228,7 @@ private string getExportRelativePath(JsonValue part) {
result.matches(".%")
}
module MainModulePath {
deprecated module MainModulePath {
/** Gets the path to the main entry point of `pkg`. */
MainModulePath of(PackageJson pkg) { result = of(pkg, ".") }
@@ -244,7 +244,7 @@ module MainModulePath {
* These files are often imported directly from a client when a "main" module is not specified.
* For performance reasons this only exists if there is no "main" field in the `package.json` file.
*/
private class FilesPath extends PathExpr, @json_string {
deprecated private class FilesPath extends PathExpr, @json_string {
PackageJson pkg;
FilesPath() {
@@ -263,7 +263,7 @@ private class FilesPath extends PathExpr, @json_string {
}
}
private module FilesPath {
deprecated private module FilesPath {
FilesPath of(PackageJson pkg) { result.getPackageJson() = pkg }
}
@@ -271,7 +271,7 @@ private module FilesPath {
* A JSON string in a `package.json` file specifying the path of the
* TypeScript typings entry point.
*/
class TypingsModulePathString extends PathString {
deprecated class TypingsModulePathString extends PathString {
PackageJson pkg;
TypingsModulePathString() {
@@ -288,7 +288,7 @@ class TypingsModulePathString extends PathString {
}
/** Companion module to the `TypingsModulePathString` class. */
module TypingsModulePathString {
deprecated module TypingsModulePathString {
/** Get the typings path for the given `package.json` file. */
TypingsModulePathString of(PackageJson pkg) { result.getPackageJson() = pkg }
}

View File

@@ -6,6 +6,7 @@
import javascript
private import semmle.javascript.internal.CachedStages
private import semmle.javascript.internal.paths.PackageJsonEx
/**
* Gets a parameter that is a library input to a top-level package.
@@ -126,19 +127,12 @@ private DataFlow::Node getAValueExportedByPackage() {
// ....
// }));
// ```
// Such files are not recognized as modules, so we manually use `NodeModule::resolveMainModule` to resolve the file against a `package.json` file.
// Such files are not recognized as modules, so we manually use `PackageJsonEx` to resolve the file against a `package.json` file.
exists(ImmediatelyInvokedFunctionExpr func, DataFlow::ParameterNode factory, int i |
factory.getName() = "factory" and
func.getParameter(i) = factory.getParameter() and
DataFlow::globalVarRef("define").getACall().getAnArgument() = factory.getALocalUse() and
func.getFile() =
min(int j, File f |
f =
NodeModule::resolveMainModule(any(PackageJson pack | exists(pack.getPackageName())), j,
".")
|
f order by j
)
func.getFile() = any(PackageJsonEx pack).getMainFileOrBestGuess()
|
result = func.getInvocation().getArgument(i).flow().getAFunctionValue().getAReturn()
or

View File

@@ -9,13 +9,13 @@ private import semmle.javascript.dataflow.internal.DataFlowNode
/**
* Internal representation of paths as lists of components.
*/
private newtype TPath =
deprecated private newtype TPath =
/** A root path. */
TRootPath(string root) {
root = any(Folder f | not exists(f.getParentContainer())).getAbsolutePath()
} or
/** A path of the form `<parent>/<component>`. */
TConsPath(Path parent, string component) {
deprecated TConsPath(Path parent, string component) {
// make sure we can represent paths of files in snapshot
exists(Folder f | f = parent.getContainer() | exists(f.getChildContainer(component)))
or
@@ -32,7 +32,7 @@ private newtype TPath =
* Gets a textual representation of path `p` using slashes as delimiters;
* the empty path is represented as the empty string `""`.
*/
private string pp(TPath p) {
deprecated private string pp(TPath p) {
p = TRootPath(result + "/")
or
exists(TPath parent, string component | p = TConsPath(parent, component) |
@@ -45,7 +45,7 @@ private string pp(TPath p) {
* which may (but does not have to) correspond to a file or folder included
* in the snapshot.
*/
class Path extends TPath {
deprecated class Path extends TPath {
/**
* Gets the file or folder referred to by this path, if it exists.
*/
@@ -60,14 +60,14 @@ class Path extends TPath {
/**
* The empty path, which refers to the file system root.
*/
private class RootPath extends Path, TRootPath {
deprecated private class RootPath extends Path, TRootPath {
override string toString() { this = TRootPath(result) }
}
/**
* A non-empty path of the form `<parent>/<component>`.
*/
private class ConsPath extends Path, TConsPath {
deprecated private class ConsPath extends Path, TConsPath {
/** Gets the parent path of this path. */
Path getParent() { this = TConsPath(result, _) }
@@ -96,27 +96,22 @@ private class ConsPath extends Path, TConsPath {
private string pathRegex() { result = "(.*)(?:/|^)(([^/]*?)(\\.([^.]*))?)" }
/**
* A string value that represents a (relative or absolute) file system path.
*
* Each path string is associated with one or more root folders relative to
* which the path may be resolved. For instance, paths inside a module are
* usually resolved relative to the module's folder, with a default
* lookup path as the fallback.
* A `string` with some additional member predicates for extracting parts of a file path.
*/
abstract class PathString extends string {
class FilePath extends string {
bindingset[this]
PathString() { any() }
/** Gets a root folder relative to which this path can be resolved. */
abstract Folder getARootFolder();
FilePath() { any() }
/** Gets the `i`th component of this path. */
bindingset[this]
string getComponent(int i) { result = this.splitAt("/", i) }
/** Gets the number of components of this path. */
bindingset[this]
int getNumComponent() { result = count(int i | exists(this.getComponent(i))) }
/** Gets the base name of the folder or file this path refers to. */
bindingset[this]
string getBaseName() { result = this.regexpCapture(pathRegex(), 2) }
/**
@@ -124,9 +119,11 @@ abstract class PathString extends string {
* up to (but not including) the last dot character if there is one, or the entire
* base name if there is not
*/
bindingset[this]
string getStem() { result = this.regexpCapture(pathRegex(), 3) }
/** Gets the path of the parent folder of the folder or file this path refers to. */
bindingset[this]
string getDirName() { result = this.regexpCapture(pathRegex(), 1) }
/**
@@ -135,8 +132,51 @@ abstract class PathString extends string {
*
* Has no result if the base name does not contain a dot.
*/
bindingset[this]
string getExtension() { result = this.regexpCapture(pathRegex(), 4) }
/**
* Holds if this is a relative path starting with an explicit `./` or similar syntax meaning it
* must be resolved relative to its enclosing folder.
*
* Specifically this holds when the string is `.` or `..`, or starts with `./` or `../` or
* `.\` or `..\`.
*/
bindingset[this]
pragma[inline_late]
predicate isDotRelativePath() { this.regexpMatch("\\.\\.?(?:[/\\\\].*)?") }
/**
* Gets the NPM package name from the beginning of the given import path.
*
* Has no result for paths starting with a `.` or `/`
*
* For example:
* - `foo/bar` maps to `foo`
* - `@example/foo/bar` maps to `@example/foo`
* - `./foo` maps to nothing.
*/
bindingset[this]
string getPackagePrefix() {
result = this.regexpFind("^(@[^/\\\\]+[/\\\\])?[^@./\\\\][^/\\\\]*", _, _)
}
}
/**
* A string value that represents a (relative or absolute) file system path.
*
* Each path string is associated with one or more root folders relative to
* which the path may be resolved. For instance, paths inside a module are
* usually resolved relative to the module's folder, with a default
* lookup path as the fallback.
*/
abstract deprecated class PathString extends FilePath {
bindingset[this]
PathString() { any() }
/** Gets a root folder relative to which this path can be resolved. */
abstract Folder getARootFolder();
/**
* Gets the absolute path that the sub-path consisting of the first `n`
* components of this path refers to when resolved relative to the
@@ -156,7 +196,7 @@ abstract class PathString extends string {
* components of this path refers to when resolved relative to the
* given `root` folder.
*/
private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) {
deprecated private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) {
n = 0 and result.getContainer() = root and root = p.getARootFolder() and inTS = false
or
exists(Path base, string next | next = getComponent(p, n - 1, base, root, inTS) |
@@ -185,7 +225,7 @@ private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) {
* Supports that the root directory might be compiled output from TypeScript.
* `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`.
*/
private string getComponent(PathString str, int n, Path base, Folder root, boolean inTS) {
deprecated private string getComponent(PathString str, int n, Path base, Folder root, boolean inTS) {
exists(boolean prevTS |
base = resolveUpTo(str, n, root, prevTS) and
(
@@ -208,7 +248,7 @@ private string getComponent(PathString str, int n, Path base, Folder root, boole
/**
* Predicates for resolving imports to compiled TypeScript.
*/
private module TypeScriptOutDir {
deprecated private module TypeScriptOutDir {
/**
* Gets a folder of TypeScript files that is compiled to JavaScript files in `outdir` relative to a `parent`.
*/
@@ -300,7 +340,7 @@ private module TypeScriptOutDir {
* as their highest-priority root, with default library paths as additional roots
* of lower priority.
*/
abstract class PathExpr extends Locatable {
abstract deprecated class PathExpr extends Locatable {
/** Gets the (unresolved) path represented by this expression. */
abstract string getValue();
@@ -373,7 +413,7 @@ abstract class PathExpr extends Locatable {
}
/** A path string derived from a path expression. */
private class PathExprString extends PathString {
deprecated private class PathExprString extends PathString {
PathExprString() { this = any(PathExpr pe).getValue() }
override Folder getARootFolder() {
@@ -382,13 +422,13 @@ private class PathExprString extends PathString {
}
pragma[nomagic]
private EarlyStageNode getAPathExprAlias(PathExpr expr) {
deprecated private EarlyStageNode getAPathExprAlias(PathExpr expr) {
DataFlow::Impl::earlyStageImmediateFlowStep(TValueNode(expr), result)
or
DataFlow::Impl::earlyStageImmediateFlowStep(getAPathExprAlias(expr), result)
}
private class PathExprFromAlias extends PathExpr {
deprecated private class PathExprFromAlias extends PathExpr {
private PathExpr other;
PathExprFromAlias() { TValueNode(this) = getAPathExprAlias(other) }
@@ -404,7 +444,7 @@ private class PathExprFromAlias extends PathExpr {
* A path expression of the form `p + q`, where both `p` and `q`
* are path expressions.
*/
private class ConcatPath extends PathExpr {
deprecated private class ConcatPath extends PathExpr {
ConcatPath() {
exists(AddExpr add | this = add |
add.getLeftOperand() instanceof PathExpr and

View File

@@ -0,0 +1,222 @@
/**
* Provides a class for working with `tsconfig.json` files.
*/
private import javascript
private import semmle.javascript.internal.paths.PathMapping
/**
* A TypeScript configuration file, usually named `tsconfig.json`.
*/
class TSConfig extends JsonObject {
TSConfig() {
this.getJsonFile().getBaseName().matches("%tsconfig%.json") and
this.isTopLevel()
}
/** Gets the folder containing this file. */
Folder getFolder() { result = this.getJsonFile().getParentContainer() }
/** Gets the `compilerOptions` object. */
JsonObject getCompilerOptions() { result = this.getPropValue("compilerOptions") }
/** Gets the string value in the `extends` property. */
string getExtendsPath() { result = this.getPropStringValue("extends") }
/** Gets the file referred to by the `extends` property. */
File getExtendedFile() { result = Resolver::resolve(this.getFolder(), this.getExtendsPath()) }
/** Gets the `TSConfig` file referred to by the `extends` property. */
TSConfig getExtendedTSConfig() { result.getJsonFile() = this.getExtendedFile() }
/** Gets the string value in the `baseUrl` property. */
string getBaseUrlPath() { result = this.getCompilerOptions().getPropStringValue("baseUrl") }
/** Gets the folder referred to by the `baseUrl` property in this file, not taking `extends` into account. */
Folder getOwnBaseUrlFolder() {
result = Resolver::resolve(this.getFolder(), this.getBaseUrlPath())
}
/** Gets the effective baseUrl folder for this tsconfig file. */
Folder getBaseUrlFolder() {
result = this.getOwnBaseUrlFolder()
or
not exists(this.getOwnBaseUrlFolder()) and
result = this.getExtendedTSConfig().getBaseUrlFolder()
}
/** Gets the effective baseUrl folder for this tsconfig file, or its enclosing folder if there is no baseUrl. */
Folder getBaseUrlFolderOrOwnFolder() {
result = this.getBaseUrlFolder()
or
not exists(this.getBaseUrlFolder()) and
result = this.getFolder()
}
/** Gets a path mentioned in the `include` property. */
string getAnIncludePath() {
result = this.getPropStringValue("include")
or
result = this.getPropValue("include").(JsonArray).getElementStringValue(_)
}
/**
* Gets a file or folder refenced by a path the `include` property, possibly
* inherited from an extended tsconfig file.
*
* Does not include all the files within includes directories, use `getAnIncludedContainer` for that.
*/
Container getAnIncludePathTarget() {
result = Resolver::resolve(this.getFolder(), this.getAnIncludePath())
or
not exists(this.getPropValue("include")) and
result = this.getExtendedTSConfig().getAnIncludePathTarget()
}
/**
* Gets a file or folder inside the directory tree mentioned in the `include` property.
*/
Container getAnIncludedContainer() {
result = this.getAnIncludePathTarget()
or
result = this.getAnIncludedContainer().getAChildContainer()
}
/** Gets the path mentioned in the `rootDir` property. */
string getRootDirPath() { result = this.getCompilerOptions().getPropStringValue("rootDir") }
private Container getOwnRootDir() {
result = Resolver::resolve(this.getFolder(), this.getRootDirPath())
}
/** Gets the file or folder referenced by the `rootDir` property. */
Container getRootDir() {
result = this.getOwnRootDir()
or
not exists(this.getRootDirPath()) and
result = this.getExtendedTSConfig().getOwnRootDir()
}
private string getATopLevelIncludePath() {
result = this.getAnIncludePath().(FilePath).getComponent(0)
}
private string getUniqueTopLevelIncludePath() {
result = unique( | | this.getATopLevelIncludePath())
}
/**
* Gets the folder referred to by the `rootDir` property, or if absent, an effective root dir
* derived from `include` paths.
*/
Container getEffectiveRootDir() {
result = this.getRootDir()
or
not exists(this.getRootDir()) and
(
result = this.getFolder().getFolder(this.getUniqueTopLevelIncludePath())
or
not exists(this.getUniqueTopLevelIncludePath()) and
exists(this.getATopLevelIncludePath()) and
result = this.getFolder()
or
not exists(this.getATopLevelIncludePath()) and
result = this.getExtendedTSConfig().getEffectiveRootDir()
)
}
private JsonObject getPathMappings() { result = this.getCompilerOptions().getPropValue("paths") }
/**
* Holds if this has a path mapping from `pattern` to `newPath`.
*
* For example, `"paths": { "@/*": "./src/*" }` maps the `@/*` pattern to `./src/*`.
*
* Does not include path mappings from extended tsconfig files.
*/
predicate hasPathMapping(string pattern, string newPath) {
this.getPathMappings().getPropStringValue(pattern) = newPath
or
this.getPathMappings().getPropValue(pattern).(JsonArray).getElementStringValue(_) = newPath
}
/**
* Holds if this has an exact path mapping (i.e. no wildcards) from `pattern` to `newPath`.
*
* For example, `"paths": { "@": "./src/index.ts" }` maps the `@` path to `./src/index.ts`.
*
* Does not include path mappings from extended tsconfig files.
*/
predicate hasExactPathMapping(string pattern, string newPath) {
this.hasPathMapping(pattern, newPath) and
not pattern.matches("%*%")
}
/**
* Holds if this has a path mapping from the `pattern` prefix to the `newPath` prefix.
* The trailing `*` is not included.
*
* For example, `"paths": { "@/*": "./src/*" }` maps the `@/` pattern to `./src/`.
*
* Does not include path mappings from extended tsconfig files.
*/
predicate hasPrefixPathMapping(string pattern, string newPath) {
this.hasPathMapping(pattern + "*", newPath + "*")
}
}
/** For resolving paths in a tsconfig file, except `paths` mappings. */
private module ResolverConfig implements Folder::ResolveSig {
predicate shouldResolve(Container base, string path) {
exists(TSConfig cfg |
base = cfg.getFolder() and
path =
[cfg.getExtendsPath(), cfg.getBaseUrlPath(), cfg.getRootDirPath(), cfg.getAnIncludePath()]
)
}
predicate allowGlobs() { any() } // "include" can use globs
}
private module Resolver = Folder::Resolve<ResolverConfig>;
/**
* Gets a tsconfig file to use as fallback for handling paths in `c`.
*
* This holds for files and folders where no tsconfig seems to include it,
* but it has one or more tsconfig files in parent directories.
*/
private TSConfig getFallbackTSConfig(Container c) {
not c = any(TSConfig t).getAnIncludedContainer() and
(
c = result.getFolder()
or
result = getFallbackTSConfig(c.getParentContainer())
)
}
private class TSConfigPathMapping extends PathMapping, TSConfig {
override File getAnAffectedFile() {
result = this.getAnIncludedContainer()
or
this = getFallbackTSConfig(result)
}
override predicate hasExactPathMapping(string pattern, Container newContext, string newPath) {
exists(TSConfig tsconfig |
tsconfig = this.getExtendedTSConfig*() and
tsconfig.hasExactPathMapping(pattern, newPath) and
newContext = tsconfig.getBaseUrlFolderOrOwnFolder()
)
}
override predicate hasPrefixPathMapping(string pattern, Container newContext, string newPath) {
exists(TSConfig tsconfig |
tsconfig = this.getExtendedTSConfig*() and
tsconfig.hasPrefixPathMapping(pattern, newPath) and
newContext = tsconfig.getBaseUrlFolderOrOwnFolder()
)
}
override predicate hasBaseUrl(Container base) { base = this.getBaseUrlFolder() }
}

View File

@@ -207,7 +207,7 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference {
/** Gets the expression specifying the module. */
Expr getExpression() { result = this.getChildExpr(0) }
override PathExpr getImportedPath() { result = this.getExpression() }
override Expr getImportedPathExpr() { result = this.getExpression() }
override Module getEnclosingModule() { result = this.getTopLevel() }
@@ -221,7 +221,7 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference {
}
/** A literal path expression appearing in an external module reference. */
private class LiteralExternalModulePath extends PathExpr, ConstantString {
deprecated private class LiteralExternalModulePath extends PathExpr, ConstantString {
LiteralExternalModulePath() {
exists(ExternalModuleReference emr | this.getParentExpr*() = emr.getExpression())
}
@@ -743,7 +743,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
* For non-relative imports, it is the import path itself.
*/
private string getImportName(Import imprt) {
exists(string path | path = imprt.getImportedPath().getValue() |
exists(string path | path = imprt.getImportedPathString() |
if path.regexpMatch("[./].*")
then result = imprt.getImportedModule().getFile().getRelativePath()
else result = path
@@ -1731,7 +1731,7 @@ class TSGlobalDeclImport extends DataFlow::ModuleImportNode::Range {
pkg = tt.getExpressionName() and
// then, check pkg is imported as "import * as pkg from path"
i.getLocal().getVariable() = pkg.getVariable() and
path = i.getImportedPath().getValue() and
path = i.getImportedPathString() and
// finally, "this" needs to be a reference to gv
this = DataFlow::exprNode(gv.getAnAccess())
)

View File

@@ -738,7 +738,7 @@ module ModuleImportNode {
DefaultRange() {
exists(Import i |
this = i.getImportedModuleNode() and
i.getImportedPath().getValue() = path
i.getImportedPathString() = path
)
or
// AMD require

View File

@@ -25,7 +25,7 @@ private class AnalyzedImportSpecifier extends AnalyzedVarDef, @import_specifier
override predicate isIncomplete(DataFlow::Incompleteness cause) {
// mark as incomplete if the import could rely on the lookup path
mayDependOnLookupPath(id.getImportedPath().getValue()) and
mayDependOnLookupPath(id.getImportedPathString()) and
cause = "import"
or
// mark as incomplete if we cannot fully analyze this import
@@ -260,7 +260,7 @@ private class AnalyzedAmdImport extends AnalyzedPropertyRead, DataFlow::Node {
Module required;
AnalyzedAmdImport() {
exists(AmdModule amd, PathExpr dep |
exists(AmdModule amd, Expr dep |
exists(Parameter p |
amd.getDefine().dependencyParameter(dep, p) and
this = DataFlow::parameterNode(p)

View File

@@ -151,6 +151,11 @@ class ExternalNpmDependency extends NpmDependency {
}
}
pragma[nomagic]
private string getPackagePrefix(Import i) {
result = i.getImportedPathString().(FilePath).getPackagePrefix()
}
/**
* Holds if import `i` may refer to the declared dependency `dep` of package `pkg`,
* where the result value is the nesting depth of the file containing `i` within `pkg`.
@@ -159,7 +164,7 @@ private int importsDependency(Import i, NpmPackage pkg, NpmDependency dep) {
exists(string name |
dep = pkg.getPackageJson().getADependenciesObject(_).getPropValue(name) and
not exists(i.getImportedModule()) and
i.getImportedPath().getComponent(0) = name and
name = getPackagePrefix(i) and
i.getEnclosingModule() = pkg.getAModule() and
result = distance(pkg, i.getFile())
)

View File

@@ -9,6 +9,7 @@ private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomiz
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.internal.PreCallGraphStep
private import semmle.javascript.ViewComponentInput
private import semmle.javascript.internal.paths.PathExprResolver
/**
* Provides classes for working with Angular (also known as Angular 2.x) applications.
@@ -240,18 +241,15 @@ module Angular2 {
class TemplateTopLevel = Templating::TemplateTopLevel;
/** The RHS of a `templateUrl` property, seen as a path expression. */
private class TemplateUrlPath extends PathExpr {
TemplateUrlPath() {
exists(Property prop |
prop.getName() = "templateUrl" and
this = prop.getInit()
)
}
override string getValue() { result = this.(Expr).getStringValue() }
private predicate shouldResolveExpr(Expr e) {
exists(Property prop |
prop.getName() = "templateUrl" and
e = prop.getInit()
)
}
private module Resolver = ResolveExpr<shouldResolveExpr/1>;
/**
* Holds if the value of `attrib` is interpreted as an Angular expression.
*/
@@ -338,7 +336,7 @@ module Angular2 {
*/
pragma[noinline]
File getTemplateFile() {
result = decorator.getOptionArgument(0, "templateUrl").asExpr().(PathExpr).resolve()
result = Resolver::resolveExpr(decorator.getOptionArgument(0, "templateUrl").asExpr())
}
/** Gets an element in the HTML template of this component. */

View File

@@ -32,7 +32,7 @@ pragma[nomagic]
private predicate isAngularTopLevel(TopLevel tl) {
exists(Import imprt |
imprt.getTopLevel() = tl and
imprt.getImportedPath().getValue() = "angular"
imprt.getImportedPathString() = "angular"
)
or
exists(GlobalVarAccess global |

View File

@@ -3,6 +3,7 @@
*/
import javascript
private import semmle.javascript.internal.paths.PathMapping
module Babel {
/**
@@ -138,7 +139,7 @@ module Babel {
/**
* An import path expression that may be transformed by `babel-plugin-root-import`.
*/
private class BabelRootTransformedPathExpr extends PathExpr, Expr {
deprecated private class BabelRootTransformedPathExpr extends PathExpr, Expr {
RootImportConfig plugin;
string prefix;
string mappedPrefix;
@@ -166,7 +167,7 @@ module Babel {
/**
* An import path transformed by `babel-plugin-root-import`.
*/
private class BabelRootTransformedPath extends PathString {
deprecated private class BabelRootTransformedPath extends PathString {
BabelRootTransformedPathExpr pathExpr;
BabelRootTransformedPath() { this = pathExpr.getValue() }
@@ -202,4 +203,12 @@ module Babel {
)
}
}
private class BabelPathMapping extends PathMapping, RootImportConfig {
override File getAnAffectedFile() { result = this.getConfig().getAContainerInScope() }
override predicate hasPrefixPathMapping(string pattern, Container newContext, string newPath) {
newPath = this.getRoot(pattern) and newContext = this.getFolder()
}
}
}

View File

@@ -36,7 +36,7 @@ module LazyCache {
override Module getEnclosingModule() { result = this.getTopLevel() }
override PathExpr getImportedPath() { result = this.getArgument(0) }
override Expr getImportedPathExpr() { result = this.getArgument(0) }
private LazyCacheVariable getVariable() { result = cache }
@@ -58,7 +58,7 @@ module LazyCache {
}
/** A constant path element appearing in a call to a lazy-cache object. */
private class LazyCachePathExpr extends PathExpr, ConstantString {
deprecated private class LazyCachePathExpr extends PathExpr, ConstantString {
LazyCachePathExpr() { this = any(LazyCacheImport rp).getArgument(0) }
override string getValue() { result = this.getStringValue() }

View File

@@ -733,7 +733,7 @@ private class ReactRouterSource extends ClientSideRemoteFlowSource {
* Holds if `mod` transitively depends on `react-router-dom`.
*/
private predicate dependsOnReactRouter(Module mod) {
mod.getAnImport().getImportedPath().getValue() = "react-router-dom"
mod.getAnImport().getImportedPathString() = "react-router-dom"
or
dependsOnReactRouter(mod.getAnImportedModule())
}

View File

@@ -442,7 +442,7 @@ module Vue {
override DataFlow::SourceNode getASource() {
exists(Import imprt |
imprt.getImportedPath().resolve() instanceof VueFile and
imprt.getImportedFile() instanceof VueFile and
result = imprt.getImportedModuleNode()
)
}
@@ -494,7 +494,7 @@ module Vue {
// There is no explicit `new Vue()` call in .vue files, so instead get all the imports
// of the .vue file.
exists(Import imprt |
imprt.getImportedPath().resolve() = file and
imprt.getImportedFile() = file and
result.asSource() = imprt.getImportedModuleNode()
)
}

View File

@@ -58,7 +58,7 @@ predicate parseTypeString(string rawType, string package, string qualifiedName)
predicate isPackageUsed(string package) {
package = "global"
or
package = any(JS::Import imp).getImportedPath().getValue()
package = any(JS::Import imp).getImportedPathString()
or
any(JS::TypeName t).hasQualifiedName(package, _)
or

View File

@@ -0,0 +1,70 @@
/**
* Provides predicates for use in `Folder::ResolveSig` in order to resolve
* paths using JavaScript semantics.
*/
private import javascript
private import semmle.javascript.TSConfig
/**
* Gets a folder name that is a common source folder name.
*/
string getASrcFolderName() { result = ["ts", "js", "src", "lib"] }
/**
* Gets a folder name that is a common build output folder name.
*/
string getABuildOutputFolderName() { result = ["dist", "build", "out", "lib"] }
/**
* Provides predicates for use in a `Folder::ResolveSig` in order to resolve
* paths using JavaScript semantics.
*
* This accounts for two things:
* - automatic file extensions (e.g `./foo` may resolve to `./foo.js`)
* - mapping compiled-generated files back to their original source files
*/
module JSPaths {
private Container getAnAdditionalChildFromBuildMapping(Container base, string name) {
// When importing a .js file, map to the original file that compiles to the .js file.
exists(string stem |
result = base.(Folder).getJavaScriptFileOrTypings(stem) and
name = stem + ".js"
)
or
// Redirect './bar' to 'foo' given a tsconfig like:
//
// { include: ["foo"], compilerOptions: { outDir: "./bar" }}
//
exists(TSConfig tsconfig |
name =
tsconfig.getCompilerOptions().getPropStringValue("outDir").regexpReplaceAll("^\\./", "") and
base = tsconfig.getFolder() and
result = tsconfig.getEffectiveRootDir()
)
}
/**
* Gets an additional child of `base` to include when resolving JS paths.
*/
pragma[nomagic]
Container getAnAdditionalChild(Container base, string name) {
// Automatically fill in file extensions
result = base.(Folder).getJavaScriptFileOrTypings(name)
or
result = getAnAdditionalChildFromBuildMapping(base, name)
or
// Heuristic version of the above based on commonly used source and build folder names
not exists(getAnAdditionalChildFromBuildMapping(base, name)) and
exists(Folder folder | base = folder |
folder = any(PackageJson pkg).getFolder() and
name = getABuildOutputFolderName() and
not exists(folder.getJavaScriptFileOrTypings(name)) and
(
result = folder.getChildContainer(getASrcFolderName())
or
result = folder
)
)
}
}

View File

@@ -0,0 +1,151 @@
private import javascript
private import semmle.javascript.internal.paths.JSPaths
/**
* A `package.json` file. The class is an extension of the `PackageJson` class with some internal path-resolution predicates.
*/
class PackageJsonEx extends PackageJson {
private JsonValue getAPartOfExportsSection(string pattern) {
result = this.getPropValue("exports") and
pattern = ""
or
exists(string prop, string prevPath |
result = this.getAPartOfExportsSection(prevPath).getPropValue(prop) and
if prop.matches("./%") then pattern = prop.suffix(2) else pattern = prevPath
)
}
predicate hasPathMapping(string pattern, string newPath) {
this.getAPartOfExportsSection(pattern).getStringValue() = newPath
}
predicate hasExactPathMapping(string pattern, string newPath) {
this.getAPartOfExportsSection(pattern).getStringValue() = newPath and
not pattern.matches("%*%")
}
predicate hasPrefixPathMapping(string pattern, string newPath) {
this.hasPathMapping(pattern + "*", newPath + "*")
}
predicate hasExactPathMappingTo(string pattern, Container target) {
exists(string newPath |
this.hasExactPathMapping(pattern, newPath) and
target = Resolver::resolve(this.getFolder(), newPath)
)
}
predicate hasPrefixPathMappingTo(string pattern, Container target) {
exists(string newPath |
this.hasPrefixPathMapping(pattern, newPath) and
target = Resolver::resolve(this.getFolder(), newPath)
)
}
string getMainPath() { result = this.getPropStringValue(["main", "module"]) }
File getMainFile() {
exists(Container main | main = Resolver::resolve(this.getFolder(), this.getMainPath()) |
result = main
or
result = main.(Folder).getJavaScriptFileOrTypings("index")
)
}
File getMainFileOrBestGuess() {
result = this.getMainFile()
or
result = guessPackageJsonMain1(this)
or
result = guessPackageJsonMain2(this)
}
string getAPathInFilesArray() {
result = this.getPropValue("files").(JsonArray).getElementStringValue(_)
}
Container getAFileInFilesArray() {
result = Resolver::resolve(this.getFolder(), this.getAPathInFilesArray())
}
override File getTypingsFile() {
result = Resolver::resolve(this.getFolder(), this.getTypings())
or
not exists(this.getTypings()) and
exists(File mainFile |
mainFile = this.getMainFileOrBestGuess() and
result =
mainFile
.getParentContainer()
.getFile(mainFile.getStem().regexpReplaceAll("\\.d$", "") + ".d.ts")
)
}
}
private module ResolverConfig implements Folder::ResolveSig {
additional predicate shouldResolve(PackageJsonEx pkg, Container base, string path) {
base = pkg.getFolder() and
(
pkg.hasExactPathMapping(_, path)
or
pkg.hasPrefixPathMapping(_, path)
or
path = pkg.getMainPath()
or
path = pkg.getAPathInFilesArray()
or
path = pkg.getTypings()
)
}
predicate shouldResolve(Container base, string path) { shouldResolve(_, base, path) }
predicate getAnAdditionalChild = JSPaths::getAnAdditionalChild/2;
predicate isOptionalPathComponent(string segment) {
// Try to omit paths can might refer to a build format, .e.g `dist/cjs/foo.cjs` -> `src/foo.ts`
segment = ["cjs", "mjs", "js"]
}
bindingset[segment]
string rewritePathSegment(string segment) {
// Try removing anything after the first dot, such as foo.min.js -> foo (the extension is then filled in by getAdditionalChild)
result = segment.regexpReplaceAll("\\..*", "")
}
}
private module Resolver = Folder::Resolve<ResolverConfig>;
/**
* Removes the scope from a package name, e.g. `@foo/bar` -> `bar`.
*/
bindingset[name]
private string stripPackageScope(string name) { result = name.regexpReplaceAll("^@[^/]+/", "") }
private predicate isImplementationFile(File f) { not f.getBaseName().matches("%.d.ts") }
File guessPackageJsonMain1(PackageJsonEx pkg) {
not isImplementationFile(pkg.getMainFile()) and
exists(Folder folder, Folder subfolder |
folder = pkg.getFolder() and
(
subfolder = folder or
subfolder = folder.getChildContainer(getASrcFolderName()) or
subfolder =
folder
.getChildContainer(getASrcFolderName())
.(Folder)
.getChildContainer(getASrcFolderName())
)
|
result = subfolder.getJavaScriptFileOrTypings("index")
or
result = subfolder.getJavaScriptFileOrTypings(stripPackageScope(pkg.getDeclaredPackageName()))
)
}
File guessPackageJsonMain2(PackageJsonEx pkg) {
not isImplementationFile(pkg.getMainFile()) and
not isImplementationFile(guessPackageJsonMain1(pkg)) and
result = pkg.getAFileInFilesArray()
}

View File

@@ -0,0 +1,36 @@
private import javascript
/**
* A path expression that can be constant-folded by concatenating subexpressions.
*/
abstract class PathConcatenation extends Expr {
/** Gets the separator to insert between paths */
string getSeparator() { result = "" }
/** Gets the `n`th operand to concatenate. */
abstract Expr getOperand(int n);
}
private class AddExprConcatenation extends PathConcatenation, AddExpr {
override Expr getOperand(int n) {
n = 0 and result = this.getLeftOperand()
or
n = 1 and result = this.getRightOperand()
}
}
private class TemplateConcatenation extends PathConcatenation, TemplateLiteral {
override Expr getOperand(int n) { result = this.getElement(n) }
}
private class JoinCallConcatenation extends PathConcatenation, CallExpr {
JoinCallConcatenation() {
// Heuristic recognition of path.join and path.resolve since we can't rely on SourceNode at this stage.
this.getReceiver().(VarAccess).getName() = "path" and
this.getCalleeName() = ["join", "resolve"]
}
override Expr getOperand(int n) { result = this.getArgument(n) }
override string getSeparator() { result = "/" }
}

View File

@@ -0,0 +1,250 @@
private import javascript
private import semmle.javascript.internal.paths.PackageJsonEx
private import semmle.javascript.internal.paths.JSPaths
private import semmle.javascript.internal.paths.PathMapping
private import semmle.javascript.internal.paths.PathConcatenation
private import semmle.javascript.dataflow.internal.DataFlowNode
/**
* Gets the file to import when an imported path resolves to the given `folder`.
*/
File getFileFromFolderImport(Folder folder) {
result = folder.getJavaScriptFileOrTypings("index")
or
// Note that unlike "exports" paths, "main" and "module" also take effect when the package
// is imported via a relative path, e.g. `require("..")` targeting a folder with a package.json file.
exists(PackageJsonEx pkg |
pkg.getFolder() = folder and
result = pkg.getMainFileOrBestGuess()
)
}
private Variable dirnameVar() { result.getName() = "__dirname" }
private Variable filenameVar() { result.getName() = "__filename" }
private signature predicate exprSig(Expr e);
module ResolveExpr<exprSig/1 shouldResolveExpr> {
/** Holds if we need the constant string-value of `node`. */
private predicate needsConstantFolding(EarlyStageNode node) {
exists(Expr e |
shouldResolveExpr(e) and
node = TValueNode(e)
)
or
exists(EarlyStageNode needsFolding | needsConstantFolding(needsFolding) |
DataFlow::Impl::earlyStageImmediateFlowStep(node, needsFolding)
or
exists(PathConcatenation joiner |
needsFolding = TValueNode(joiner) and
node = TValueNode(joiner.getOperand(_))
)
)
}
/** Gets the constant-value of `node` */
language[monotonicAggregates]
private string getValue(EarlyStageNode node) {
needsConstantFolding(node) and
(
exists(Expr e | node = TValueNode(e) |
result = e.getStringValue()
or
e = dirnameVar().getAnAccess() and
result = "./" // Ensure the path gets interpreted relative to the current directory
or
e = filenameVar().getAnAccess() and
result = "./" + e.getFile().getBaseName()
)
or
exists(EarlyStageNode pred |
DataFlow::Impl::earlyStageImmediateFlowStep(pred, node) and
result = getValue(pred)
)
or
exists(PathConcatenation join |
node = TValueNode(join) and
// Note: 'monotonicAggregates' above lets us use recursion in the last clause of this aggregate
result =
strictconcat(int n, EarlyStageNode child, string sep |
child = TValueNode(join.getOperand(n)) and sep = join.getSeparator()
|
getValue(child), sep order by n
)
)
)
}
final private class FinalExpr = Expr;
private class RelevantExpr extends FinalExpr {
RelevantExpr() { shouldResolveExpr(this) }
/** Gets the string-value of this path. */
string getValue() { result = getValue(TValueNode(this)) }
/** Gets a path mapping affecting this path. */
pragma[nomagic]
PathMapping getAPathMapping() { result.getAnAffectedFile() = this.getFile() }
/** Gets the NPM package name from the beginning of this path. */
pragma[nomagic]
string getPackagePrefix() { result = this.getValue().(FilePath).getPackagePrefix() }
}
/**
* Holds if `expr` matches a path mapping, and should thus be resolved as `newPath` relative to `base`.
*/
pragma[nomagic]
private predicate resolveViaPathMapping(RelevantExpr expr, Container base, string newPath) {
// Handle path mappings such as `{ "paths": { "@/*": "./src/*" }}` in a tsconfig.json file
exists(PathMapping mapping, string value |
mapping = expr.getAPathMapping() and
value = expr.getValue()
|
mapping.hasExactPathMapping(value, base, newPath)
or
exists(string pattern, string suffix, string mappedPath |
mapping.hasPrefixPathMapping(pattern, base, mappedPath) and
value = pattern + suffix and
newPath = mappedPath + suffix
)
)
or
// Handle imports referring to a package by name, where we have a package.json
// file for that package in the codebase. This is treated separately from PathMapping for performance
// reasons, as there can be a large number of packages which affect all files in the project.
//
// This part only handles the "exports" property of package.json. "main" and "modules" are
// handled further down because their semantics are easier to handle there.
exists(PackageJsonEx pkg, string packageName, string remainder |
packageName = expr.getPackagePrefix() and
pkg.getDeclaredPackageName() = packageName and
remainder = expr.getValue().suffix(packageName.length()).regexpReplaceAll("^[/\\\\]", "")
|
// "exports": { ".": "./foo.js" }
// "exports": { "./foo.js": "./foo/impl.js" }
pkg.hasExactPathMappingTo(remainder, base) and
newPath = ""
or
// "exports": { "./*": "./foo/*" }
exists(string prefix |
pkg.hasPrefixPathMappingTo(prefix, base) and
remainder = prefix + newPath
)
)
}
pragma[noopt]
private predicate relativePathExpr(RelevantExpr expr, Container base, FilePath path) {
expr instanceof RelevantExpr and
path = expr.getValue() and
path.isDotRelativePath() and
exists(File file |
file = expr.getFile() and
base = file.getParentContainer()
)
}
pragma[nomagic]
private Container getJSDocProvidedModule(string moduleName) {
exists(JSDocTag tag |
tag.getTitle() = "providesModule" and
tag.getDescription().trim() = moduleName and
tag.getFile() = result
)
}
/**
* Holds if `expr` should be resolved as `path` relative to `base`.
*/
pragma[nomagic]
private predicate shouldResolve(RelevantExpr expr, Container base, FilePath path) {
// Relative paths are resolved from their enclosing folder
relativePathExpr(expr, base, path)
or
resolveViaPathMapping(expr, base, path)
or
// Resolve from baseUrl of relevant tsconfig.json file
path = expr.getValue() and
not path.isDotRelativePath() and
expr.getAPathMapping().hasBaseUrl(base)
or
// If the path starts with the name of a package, resolve relative to the directory of that package.
// Note that `getFileFromFolderImport` may subsequently redirect this to the package's "main",
// so we don't have to deal with that here.
exists(PackageJson pkg, string packageName |
packageName = expr.getPackagePrefix() and
pkg.getDeclaredPackageName() = packageName and
path = expr.getValue().suffix(packageName.length()) and
base = pkg.getFolder()
)
or
base = getJSDocProvidedModule(expr.getValue()) and
path = ""
}
private module ResolverConfig implements Folder::ResolveSig {
predicate shouldResolve(Container base, string path) { shouldResolve(_, base, path) }
predicate getAnAdditionalChild = JSPaths::getAnAdditionalChild/2;
}
private module Resolver = Folder::Resolve<ResolverConfig>;
private Container resolvePathExpr1(RelevantExpr expr) {
exists(Container base, string path |
shouldResolve(expr, base, path) and
result = Resolver::resolve(base, path)
)
}
File resolveExpr(RelevantExpr expr) {
result = resolvePathExpr1(expr)
or
result = getFileFromFolderImport(resolvePathExpr1(expr))
}
module Debug {
class PathExprToDebug extends RelevantExpr {
PathExprToDebug() { this.getValue() = "vs/nls" }
}
query PathExprToDebug pathExprs() { any() }
query string getPackagePrefixFromPathExpr_(PathExprToDebug expr) {
result = expr.getPackagePrefix()
}
query predicate resolveViaPathMapping_(PathExprToDebug expr, Container base, string newPath) {
resolveViaPathMapping(expr, base, newPath)
}
query predicate shouldResolve_(PathExprToDebug expr, Container base, string newPath) {
shouldResolve(expr, base, newPath)
}
query Container resolvePathExpr1_(PathExprToDebug expr) { result = resolvePathExpr1(expr) }
query File resolveExpr_(PathExprToDebug expr) { result = resolveExpr(expr) }
// Some predicates that are usually small enough that they don't need restriction
query File getPackageMainFile(PackageJsonEx pkg) { result = pkg.getMainFile() }
query predicate guessPackageJsonMain1_ = guessPackageJsonMain1/1;
query predicate guessPackageJsonMain2_ = guessPackageJsonMain2/1;
query predicate getFileFromFolderImport_ = getFileFromFolderImport/1;
}
}
private predicate isImportPathExpr(Expr e) {
e = any(Import imprt).getImportedPathExpr()
or
e = any(ReExportDeclaration decl).getImportedPath()
}
/** Resolves paths in imports and re-exports. */
module ImportPathResolver = ResolveExpr<isImportPathExpr/1>;

View File

@@ -0,0 +1,31 @@
/**
* Provides an extensible mechanism for modeling path mappings.
*/
private import javascript
private import semmle.javascript.TSConfig
/**
* A `tsconfig.json`-like configuration object that can affect import resolution via path mappings.
*/
abstract class PathMapping extends Locatable {
/**
* Gets a file affected by this path mapping.
*/
abstract File getAnAffectedFile();
/**
* Holds if imports paths exactly matching `pattern` should be redirected to `newPath`
* resolved relative to `newContext`.
*/
predicate hasExactPathMapping(string pattern, Container newContext, string newPath) { none() }
/**
* Holds if imports paths starting with `pattern` should have the matched prefix replaced by `newPath`
* and then resolved relative to `newContext`.
*/
predicate hasPrefixPathMapping(string pattern, Container newContext, string newPath) { none() }
/** Holds if non-relative paths in affected files should be resolved relative to `base`. */
predicate hasBaseUrl(Container base) { none() }
}

View File

@@ -88,7 +88,7 @@ module ExternalApiUsedWithUntrustedData {
not path instanceof SafeExternalApiPackage and
// Exclude paths that can be resolved to a file in the project
not exists(Import imprt |
imprt.getImportedPath().getValue() = path and exists(imprt.getImportedModule())
imprt.getImportedPathString() = path and exists(imprt.getImportedModule())
)
)
or

View File

@@ -152,4 +152,4 @@ where cycleAlert(mod, import_, importedModule, access)
select access,
access.getName() + " is uninitialized if $@ is loaded first in the cyclic import:" + " " +
repr(import_) + " => " + min(pathToModule(importedModule, mod, _)) + " => " + repr(import_) +
".", import_.getImportedPath(), importedModule.getName()
".", import_.getImportedPathExpr(), importedModule.getName()

View File

@@ -24,7 +24,7 @@ PackageJson getClosestPackageJson(Folder f) {
from Require r, string path, string mod
where
path = r.getImportedPath().getValue() and
path = r.getImportedPathString() and
// the imported module is the initial segment of the path, up to
// `/` or the end of the string, whichever comes first; we exclude
// local paths starting with `.` or `/`, since they might refer to files

View File

@@ -23,7 +23,17 @@ predicate declaresDependency(NpmPackage pkg, string name, JsonValue dep) {
/**
* Gets a path expression in a module belonging to `pkg`.
*/
PathExpr getAPathExpr(NpmPackage pkg) { result.getEnclosingModule() = pkg.getAModule() }
Expr getAPathExpr(NpmPackage pkg) {
exists(Import imprt |
result = imprt.getImportedPathExpr() and
pkg.getAModule() = imprt.getEnclosingModule()
)
or
exists(ReExportDeclaration decl |
result = decl.getImportedPath() and
pkg.getAModule() = decl.getEnclosingModule()
)
}
/**
* Gets a URL-valued attribute in a module or HTML file belonging to `pkg`.
@@ -56,9 +66,8 @@ predicate usesDependency(NpmPackage pkg, string name) {
(
// there is a path expression (e.g., in a `require` or `import`) that
// references `pkg`
exists(PathExpr path | path = getAPathExpr(pkg) |
// check whether the path is `name` or starts with `name/`, ignoring a prefix that ends with '!' (example: "scriptloader!moment")
path.getValue().regexpMatch("(.*!)?\\Q" + name + "\\E(/.*)?")
exists(Expr path | path = getAPathExpr(pkg) |
path.getStringValue().(FilePath).getPackagePrefix() = name
)
or
// there is an HTML URL attribute that may reference `pkg`

View File

@@ -18,8 +18,8 @@ query predicate amdModuleDefinition(AmdModuleDefinition mod, DataFlow::SourceNod
mod.getFactoryNode() = factory
}
query predicate amdModuleDependencies(AmdModuleDefinition mod, PathExpr dependency) {
dependency = mod.getADependency()
query predicate amdModuleDependencies(AmdModuleDefinition mod, Expr dependency) {
dependency = mod.getADependencyExpr()
}
query predicate amdModuleExportedSymbol(AmdModule m, string sym) { sym = m.getAnExportedSymbol() }

View File

@@ -1,8 +1,17 @@
nodes
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | semmle.label | [JsonObject] {compilerOptions: ...} |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | semmle.order | 1 |
| tsconfig.json:2:24:4:5 | [JsonObject] {experimentalDecorators: ...} | semmle.label | [JsonObject] {experimentalDecorators: ...} |
| tsconfig.json:3:35:3:38 | [JsonBoolean] true | semmle.label | [JsonBoolean] true |
| tsconfig.json:5:16:5:26 | [JsonArray] ["**/*.ts"] | semmle.label | [JsonArray] ["**/*.ts"] |
| tsconfig.json:5:17:5:25 | [JsonString] "**/*.ts" | semmle.label | [JsonString] "**/*.ts" |
| tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | semmle.label | [JsonArray] ["es2015", ...] |
| tsconfig.json:6:13:6:20 | [JsonString] "es2015" | semmle.label | [JsonString] "es2015" |
| tsconfig.json:6:23:6:27 | [JsonString] "dom" | semmle.label | [JsonString] "dom" |
| tst.ts:1:1:1:22 | [DeclStmt] const Dec = ... | semmle.label | [DeclStmt] const Dec = ... |
| tst.ts:1:1:1:22 | [DeclStmt] const Dec = ... | semmle.order | 1 |
| tst.ts:1:1:1:22 | [DeclStmt] const Dec = ... | semmle.order | 2 |
| tst.ts:1:7:1:9 | [VarDecl] Dec | semmle.label | [VarDecl] Dec |
| tst.ts:1:7:1:21 | [VariableDeclarator] Dec: any = null | semmle.label | [VariableDeclarator] Dec: any = null |
| tst.ts:1:12:1:14 | [KeywordTypeExpr] any | semmle.label | [KeywordTypeExpr] any |
@@ -11,7 +20,7 @@ nodes
| tst.ts:3:2:3:4 | [VarRef] Dec | semmle.label | [VarRef] Dec |
| tst.ts:3:2:3:6 | [CallExpr] Dec() | semmle.label | [CallExpr] Dec() |
| tst.ts:4:1:8:1 | [ExportDeclaration] export ... id {} } | semmle.label | [ExportDeclaration] export ... id {} } |
| tst.ts:4:1:8:1 | [ExportDeclaration] export ... id {} } | semmle.order | 2 |
| tst.ts:4:1:8:1 | [ExportDeclaration] export ... id {} } | semmle.order | 3 |
| tst.ts:4:8:8:1 | [ClassDefinition,TypeDefinition] class O ... id {} } | semmle.label | [ClassDefinition,TypeDefinition] class O ... id {} } |
| tst.ts:4:14:4:30 | [VarDecl] OperatorResolvers | semmle.label | [VarDecl] OperatorResolvers |
| tst.ts:4:32:4:31 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
@@ -30,7 +39,7 @@ nodes
| tst.ts:7:16:7:19 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void |
| tst.ts:7:21:7:22 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
| tst.ts:10:1:10:41 | [DeclStmt] const createMethodDecorator = ... | semmle.label | [DeclStmt] const createMethodDecorator = ... |
| tst.ts:10:1:10:41 | [DeclStmt] const createMethodDecorator = ... | semmle.order | 3 |
| tst.ts:10:1:10:41 | [DeclStmt] const createMethodDecorator = ... | semmle.order | 4 |
| tst.ts:10:7:10:27 | [VarDecl] createMethodDecorator | semmle.label | [VarDecl] createMethodDecorator |
| tst.ts:10:7:10:40 | [VariableDeclarator] createM ... = null | semmle.label | [VariableDeclarator] createM ... = null |
| tst.ts:10:31:10:33 | [KeywordTypeExpr] any | semmle.label | [KeywordTypeExpr] any |
@@ -38,7 +47,7 @@ nodes
| tst.ts:12:1:12:21 | [VarRef] createMethodDecorator | semmle.label | [VarRef] createMethodDecorator |
| tst.ts:12:1:14:2 | [CallExpr] createM ... { }) | semmle.label | [CallExpr] createM ... { }) |
| tst.ts:12:1:14:3 | [ExprStmt] createM ... }); | semmle.label | [ExprStmt] createM ... }); |
| tst.ts:12:1:14:3 | [ExprStmt] createM ... }); | semmle.order | 4 |
| tst.ts:12:1:14:3 | [ExprStmt] createM ... }); | semmle.order | 5 |
| tst.ts:12:23:14:1 | [ArrowFunctionExpr] ({ args ... { } | semmle.label | [ArrowFunctionExpr] ({ args ... { } |
| tst.ts:12:24:12:40 | [ObjectPattern,Parameter] { args, context } | semmle.label | [ObjectPattern,Parameter] { args, context } |
| tst.ts:12:26:12:29 | [Label] args | semmle.label | [Label] args |
@@ -56,6 +65,20 @@ edges
| file://:0:0:0:0 | (Parameters) | tst.ts:12:24:12:40 | [ObjectPattern,Parameter] { args, context } | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | tst.ts:12:43:12:46 | [SimpleParameter] next | semmle.label | 1 |
| file://:0:0:0:0 | (Parameters) | tst.ts:12:43:12:46 | [SimpleParameter] next | semmle.order | 1 |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:2:24:4:5 | [JsonObject] {experimentalDecorators: ...} | semmle.label | 0 |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:2:24:4:5 | [JsonObject] {experimentalDecorators: ...} | semmle.order | 0 |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:5:16:5:26 | [JsonArray] ["**/*.ts"] | semmle.label | 1 |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:5:16:5:26 | [JsonArray] ["**/*.ts"] | semmle.order | 1 |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | semmle.label | 2 |
| tsconfig.json:1:1:7:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | semmle.order | 2 |
| tsconfig.json:2:24:4:5 | [JsonObject] {experimentalDecorators: ...} | tsconfig.json:3:35:3:38 | [JsonBoolean] true | semmle.label | 0 |
| tsconfig.json:2:24:4:5 | [JsonObject] {experimentalDecorators: ...} | tsconfig.json:3:35:3:38 | [JsonBoolean] true | semmle.order | 0 |
| tsconfig.json:5:16:5:26 | [JsonArray] ["**/*.ts"] | tsconfig.json:5:17:5:25 | [JsonString] "**/*.ts" | semmle.label | 0 |
| tsconfig.json:5:16:5:26 | [JsonArray] ["**/*.ts"] | tsconfig.json:5:17:5:25 | [JsonString] "**/*.ts" | semmle.order | 0 |
| tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | tsconfig.json:6:13:6:20 | [JsonString] "es2015" | semmle.label | 0 |
| tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | tsconfig.json:6:13:6:20 | [JsonString] "es2015" | semmle.order | 0 |
| tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | tsconfig.json:6:23:6:27 | [JsonString] "dom" | semmle.label | 1 |
| tsconfig.json:6:12:6:28 | [JsonArray] ["es2015", ...] | tsconfig.json:6:23:6:27 | [JsonString] "dom" | semmle.order | 1 |
| tst.ts:1:1:1:22 | [DeclStmt] const Dec = ... | tst.ts:1:7:1:21 | [VariableDeclarator] Dec: any = null | semmle.label | 1 |
| tst.ts:1:1:1:22 | [DeclStmt] const Dec = ... | tst.ts:1:7:1:21 | [VariableDeclarator] Dec: any = null | semmle.order | 1 |
| tst.ts:1:7:1:21 | [VariableDeclarator] Dec: any = null | tst.ts:1:7:1:9 | [VarDecl] Dec | semmle.label | 1 |

View File

@@ -22,8 +22,8 @@ query predicate test_ImportNamespaceSpecifier(ImportNamespaceSpecifier ins) { an
query predicate test_ImportSpecifiers(ImportSpecifier is, VarDecl res) { res = is.getLocal() }
query predicate test_Imports(ImportDeclaration id, PathExpr res0, int res1) {
res0 = id.getImportedPath() and res1 = count(id.getASpecifier())
query predicate test_Imports(ImportDeclaration id, Expr res0, int res1) {
res0 = id.getImportedPathExpr() and res1 = count(id.getASpecifier())
}
query predicate test_Module_exports(Module m, string name, DataFlow::Node exportValue) {

View File

@@ -0,0 +1,4 @@
{
"name": "a",
"main": "index.js"
}

View File

@@ -0,0 +1,5 @@
{
"name": "a",
"main": "index.js",
"description": "Nested version of package A"
}

View File

@@ -0,0 +1,3 @@
{
"name": "nested"
}

View File

@@ -6,6 +6,7 @@ dependencies
importedFile
| src/lib/tst2.js:1:1:1:13 | require("..") | src/index.js:0:0:0:0 | src/index.js |
| src/node_modules/nested/tst3.js:1:1:1:29 | require ... odule') | src/node_modules/third-party-module/fancy.js:0:0:0:0 | src/node_modules/third-party-module/fancy.js |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/a/index.js:0:0:0:0 | src/node_modules/a/index.js |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/nested/node_modules/a/index.js:0:0:0:0 | src/node_modules/nested/node_modules/a/index.js |
| src/node_modules/tst2.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:0:0:0:0 | src/node_modules/third-party-module/fancy.js |
| src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-module/main.js:0:0:0:0 | src/node_modules/parent-module/main.js |
@@ -16,6 +17,7 @@ importedFile
importedModule
| src/lib/tst2.js:1:1:1:13 | require("..") | src/index.js:1:1:4:0 | <toplevel> |
| src/node_modules/nested/tst3.js:1:1:1:29 | require ... odule') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | <toplevel> |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/a/index.js:1:1:1:25 | <toplevel> |
| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/nested/node_modules/a/index.js:1:1:1:25 | <toplevel> |
| src/node_modules/tst2.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | <toplevel> |
| src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-module/main.js:1:1:2:0 | <toplevel> |
@@ -29,10 +31,13 @@ modules
| src | test-package | src/test-submodule.js:1:1:3:0 | <toplevel> |
| src | test-package | src/tst2.js:1:1:1:13 | <toplevel> |
| src | test-package | src/tst.js:1:1:2:38 | <toplevel> |
| src/node_modules/a | a | src/node_modules/a/index.js:1:1:1:25 | <toplevel> |
| src/node_modules/b | b | src/node_modules/b/lib/index.js:1:1:2:0 | <toplevel> |
| src/node_modules/b | b | src/node_modules/b/lib/util.ts:1:1:2:0 | <toplevel> |
| src/node_modules/c | c | src/node_modules/c/src/index.js:1:1:2:0 | <toplevel> |
| src/node_modules/d | d | src/node_modules/d/main.js:1:1:2:0 | <toplevel> |
| src/node_modules/nested | nested | src/node_modules/nested/tst3.js:1:1:2:13 | <toplevel> |
| src/node_modules/nested/node_modules/a | a | src/node_modules/nested/node_modules/a/index.js:1:1:1:25 | <toplevel> |
| src/node_modules/parent-module | parent-module | src/node_modules/parent-module/main.js:1:1:2:0 | <toplevel> |
| src/node_modules/parent-module | parent-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | <toplevel> |
| src/node_modules/parent-module/sub-module | parent-module/sub-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | <toplevel> |
@@ -41,17 +46,22 @@ npm
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} | third-party-module | 23.4.0 |
| src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} | test-package | 0.1.0 |
getMainModule
| src/node_modules/a/package.json:1:1:4:1 | {\\n " ... x.js"\\n} | a | src/node_modules/a/index.js:1:1:1:25 | <toplevel> |
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} | b | src/node_modules/b/lib/index.js:1:1:2:0 | <toplevel> |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} | c | src/node_modules/c/src/index.js:1:1:2:0 | <toplevel> |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} | d | src/node_modules/d/main.js:1:1:2:0 | <toplevel> |
| src/node_modules/nested/node_modules/a/package.json:1:1:5:1 | {\\n " ... ge A"\\n} | a | src/node_modules/nested/node_modules/a/index.js:1:1:1:25 | <toplevel> |
| src/node_modules/parent-module/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} | parent-module | src/node_modules/parent-module/main.js:1:1:2:0 | <toplevel> |
| src/node_modules/parent-module/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} | parent-module/sub-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | <toplevel> |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} | third-party-module | src/node_modules/third-party-module/fancy.js:1:1:4:0 | <toplevel> |
| src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} | test-package | src/index.js:1:1:4:0 | <toplevel> |
packageJson
| src/node_modules/a/package.json:1:1:4:1 | {\\n " ... x.js"\\n} |
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} |
| src/node_modules/nested/node_modules/a/package.json:1:1:5:1 | {\\n " ... ge A"\\n} |
| src/node_modules/nested/package.json:1:1:3:1 | {\\n " ... sted"\\n} |
| src/node_modules/parent-module/package.json:1:1:4:1 | {\\n "na ... n.js"\\n} |
| src/node_modules/parent-module/sub-module/package.json:1:1:3:1 | {\\n "ma ... n.js"\\n} |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} |

View File

@@ -94,14 +94,12 @@ requireImport
| a.js:3:6:3:23 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | <toplevel> |
| a.js:4:6:4:29 | require ... /d.js') | ./sub/../d.js | d.js:1:1:7:15 | <toplevel> |
| a.js:7:1:7:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | <toplevel> |
| a.js:10:1:10:18 | require(__dirname) | | index.js:1:1:3:0 | <toplevel> |
| a.js:11:1:11:25 | require ... + '/e') | /e | e.js:1:1:6:0 | <toplevel> |
| a.js:12:1:12:28 | require ... + 'c') | ./sub/c | sub/c.js:1:1:4:0 | <toplevel> |
| b.js:1:1:1:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | <toplevel> |
| d.js:7:1:7:14 | require('foo') | foo | sub/f.js:1:1:4:17 | <toplevel> |
| index.js:2:1:2:41 | require ... b.js")) | /index.js/../b.js | b.js:1:1:8:0 | <toplevel> |
| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') | ./depend-on-me | mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> |
| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') | ./depend-on-me.js | mjs-files/depend-on-me.js:1:1:8:0 | <toplevel> |
| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') | ./depend-on-me.js | mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> |
| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') | ./depend-on-me.mjs | mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> |
| reexport/b.js:1:11:1:24 | require("./a") | ./a | reexport/a.js:1:1:3:1 | <toplevel> |
| sub/c.js:1:1:1:15 | require('../a') | ../a | a.js:1:1:14:0 | <toplevel> |

View File

@@ -26,7 +26,7 @@ query predicate require(Require r) { any() }
query predicate requireImport(Require r, string path, Module mod) {
exists(string fullpath, string prefix |
fullpath = r.getImportedPath().getValue() and
fullpath = r.getImportedPathString() and
sourceLocationPrefix(prefix) and
path = fullpath.replaceAll(prefix, "") and
mod = r.getImportedModule()

View File

@@ -0,0 +1 @@
import f from '~/a'; // $ importTarget=BabelRootImport/tst1/a.js

View File

@@ -0,0 +1 @@
import f from '~/a'; // $ importTarget=BabelRootImport/tst1/a.js

View File

@@ -0,0 +1,2 @@
import g from '~/b.js'; // $ importTarget=BabelRootImport/tst2/src/js/b.js
import f from '#/a'; // $ importTarget=BabelRootImport/tst1/a.js

View File

@@ -0,0 +1 @@
import greeting from '~/b.js'; // $ importTarget=BabelRootImport/tst3/src/b.js

View File

@@ -0,0 +1,2 @@
import g from '~/b.js'; // $ importTarget=BabelRootImport/tst4/src/js/b.js
import f from '#/a'; // $ importTarget=BabelRootImport/tst1/a.js

View File

@@ -0,0 +1 @@
export const x = "file.ts"

View File

@@ -0,0 +1 @@
export const x = "index.ts";

View File

@@ -0,0 +1 @@
export const x = "nostar.ts"

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1,47 @@
// Relative import
import "../base/lib/file"; // $ importTarget=BaseUrl/base/lib/file.ts
import "../base/lib/file.ts"; // $ importTarget=BaseUrl/base/lib/file.ts
import "../base/lib/file.js"; // $ importTarget=BaseUrl/base/lib/file.ts
import "../base/lib"; // $ importTarget=BaseUrl/base/lib/index.ts
import "../base/lib/index"; // $ importTarget=BaseUrl/base/lib/index.ts
import "../base/lib/index.ts"; // $ importTarget=BaseUrl/base/lib/index.ts
import "../base/lib/index.js"; // $ importTarget=BaseUrl/base/lib/index.ts
// Import relative to baseUrl
import "lib/file"; // $ importTarget=BaseUrl/base/lib/file.ts
import "lib/file.ts"; // $ importTarget=BaseUrl/base/lib/file.ts
import "lib/file.js"; // $ importTarget=BaseUrl/base/lib/file.ts
import "lib"; // $ importTarget=BaseUrl/base/lib/index.ts
import "lib/index"; // $ importTarget=BaseUrl/base/lib/index.ts
import "lib/index.ts"; // $ importTarget=BaseUrl/base/lib/index.ts
import "lib/index.js"; // $ importTarget=BaseUrl/base/lib/index.ts
// Import matching "@/*" path mapping
import "@/file"; // $ importTarget=BaseUrl/base/lib/file.ts
import "@/file.ts"; // $ importTarget=BaseUrl/base/lib/file.ts
import "@/file.js"; // $ importTarget=BaseUrl/base/lib/file.ts
import "@"; // $ importTarget=BaseUrl/base/lib/nostar.ts
import "@/index"; // $ importTarget=BaseUrl/base/lib/index.ts
import "@/index.ts"; // $ importTarget=BaseUrl/base/lib/index.ts
import "@/index.js"; // $ importTarget=BaseUrl/base/lib/index.ts
// Import matching "#/*" path mapping
import "#/file"; // $ importTarget=BaseUrl/base/lib/file.ts
import "#/file.ts"; // $ importTarget=BaseUrl/base/lib/file.ts
import "#/file.js"; // $ importTarget=BaseUrl/base/lib/file.ts
import "#/index"; // $ importTarget=BaseUrl/base/lib/index.ts
import "#/index.ts"; // $ importTarget=BaseUrl/base/lib/index.ts
import "#/index.js"; // $ importTarget=BaseUrl/base/lib/index.ts
// Import matching "^lib*" path mapping
import "^lib/file"; // $ importTarget=BaseUrl/base/lib/file.ts
import "^lib2/file"; // $ importTarget=BaseUrl/base/lib2/file.ts
// Import matching "@/*.xyz" path mapping. Note that this is not actually supported by TypeScript.
import "@/file.xyz";
import "@/file.ts.xyz";
import "@/file.js.xyz";
import "@.xyz";
import "@/index.xyz";
import "@/index.ts.xyz";
import "@/index.js.xyz";

View File

@@ -0,0 +1,14 @@
{
"include": ["."],
"compilerOptions": {
// Path mappings are resolved relative to baseUrl
"baseUrl": "./base",
"paths": {
"@/*": ["lib/*"],
"#/*": ["./lib/*"], // relative paths here are also resolved from the base url
"^lib*": ["./lib*"], // must end with "*" but not necessarily "/*"
"@/*.xyz": ["lib/*"],
"@": ["lib/nostar.ts"]
}
}
}

View File

@@ -0,0 +1,4 @@
import '..'; // $ importTarget=Basic/index.ts
import '../'; // $ importTarget=Basic/index.ts
import './..'; // $ importTarget=Basic/index.ts
import './../'; // $ importTarget=Basic/index.ts

View File

@@ -0,0 +1 @@
import './Subdir/sub'; // $ importTarget=Basic/Subdir/sub.ts

View File

@@ -0,0 +1,5 @@
// Type declaration for dual-module
export interface DualType {
prop: string;
}
export declare function helper(): DualType;

View File

@@ -0,0 +1,4 @@
// Implementation of dual-module
export function helper() {
return { prop: "implementation" };
}

View File

@@ -0,0 +1 @@
export const x: number;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1,7 @@
import "../lib/split"; // $ importTarget=DeclarationFiles/lib/split.d.ts importTarget=DeclarationFiles/lib/split.js
import "../lib/split.js"; // $ importTarget=DeclarationFiles/lib/split.js importTarget=DeclarationFiles/lib/split.d.ts
import "../lib/split.d.ts"; // $ importTarget=DeclarationFiles/lib/split.d.ts
import "../lib/typescript"; // $ importTarget=DeclarationFiles/lib/typescript.ts
import "../lib/typescript.js"; // $ importTarget=DeclarationFiles/lib/typescript.ts
import "../lib/typescript.d.ts"; // $ importTarget=DeclarationFiles/lib/typescript.d.ts

View File

@@ -0,0 +1,15 @@
// Test for imports using __dirname
const path = require('path');
require(__dirname + '/target.js'); // $ importTarget=DirnameImports/target.js
require(__dirname + '/nested/target.js'); // $ importTarget=DirnameImports/nested/target.js
require(__dirname + '/../import-packages.ts'); // $ importTarget=import-packages.ts
require(__dirname + '/' + 'target.js'); // $ importTarget=DirnameImports/target.js
require(path.join(__dirname, 'target.js')); // $ importTarget=DirnameImports/target.js
require(path.resolve(__dirname, 'target.js')); // $ importTarget=DirnameImports/target.js
const subdir = 'nested';
require(__dirname + '/' + subdir + '/target.js'); // $ importTarget=DirnameImports/nested/target.js
require(`${__dirname}/target.js`); // $ importTarget=DirnameImports/target.js

View File

@@ -0,0 +1,2 @@
// Nested target file for __dirname imports
module.exports = { name: 'nested-target' };

View File

@@ -0,0 +1,2 @@
// Target file for __dirname imports
module.exports = { name: 'target' };

View File

@@ -0,0 +1,3 @@
{
"main": "./dist/src/foo.js"
}

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
// The baseUrl is interpreted relative to this file, not the "root" tsconfig.json file extending it.
"baseUrl": "./../",
"paths": {
"@/*": ["lib/*"],
"@/*.xyz": ["lib/*"],
"@": ["lib/nostar.ts"]
}
}
}

View File

@@ -0,0 +1 @@
export const x = "file.ts"

View File

@@ -0,0 +1 @@
export const x = "index.ts";

View File

@@ -0,0 +1 @@
export const x = "nostar.ts"

View File

@@ -0,0 +1,35 @@
// Relative import
import "../lib/file"; // $ importTarget=Extended/lib/file.ts
import "../lib/file.ts"; // $ importTarget=Extended/lib/file.ts
import "../lib/file.js"; // $ importTarget=Extended/lib/file.ts
import "../lib"; // $ importTarget=Extended/lib/index.ts
import "../lib/index"; // $ importTarget=Extended/lib/index.ts
import "../lib/index.ts"; // $ importTarget=Extended/lib/index.ts
import "../lib/index.js"; // $ importTarget=Extended/lib/index.ts
// Import relative to baseUrl
import "lib/file"; // $ importTarget=Extended/lib/file.ts
import "lib/file.ts"; // $ importTarget=Extended/lib/file.ts
import "lib/file.js"; // $ importTarget=Extended/lib/file.ts
import "lib"; // $ importTarget=Extended/lib/index.ts
import "lib/index"; // $ importTarget=Extended/lib/index.ts
import "lib/index.ts"; // $ importTarget=Extended/lib/index.ts
import "lib/index.js"; // $ importTarget=Extended/lib/index.ts
// Import matching "@/*" path mapping
import "@/file"; // $ importTarget=Extended/lib/file.ts
import "@/file.ts"; // $ importTarget=Extended/lib/file.ts
import "@/file.js"; // $ importTarget=Extended/lib/file.ts
import "@"; // $ importTarget=Extended/lib/nostar.ts
import "@/index"; // $ importTarget=Extended/lib/index.ts
import "@/index.ts"; // $ importTarget=Extended/lib/index.ts
import "@/index.js"; // $ importTarget=Extended/lib/index.ts
// Import matching "@/*.xyz" path mapping. Note that this is not actually supported by TypeScript.
import "@/file.xyz";
import "@/file.ts.xyz";
import "@/file.js.xyz";
import "@.xyz";
import "@/index.xyz";
import "@/index.ts.xyz";
import "@/index.js.xyz";

View File

@@ -0,0 +1,4 @@
{
"include": ["."],
"extends": "./base/tsconfig.base.json"
}

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1 @@
export const x = 1;

View File

@@ -0,0 +1,10 @@
import "@/both" // $ importTarget=Fallback/lib1/both.ts SPURIOUS: importTarget=Fallback/lib2/both.ts
import "@/only1" // $ importTarget=Fallback/lib1/only1.ts
import "@/only2" // $ importTarget=Fallback/lib2/only2.ts
import "@/differentExtension" // $ importTarget=Fallback/lib2/differentExtension.ts SPURIOUS: importTarget=Fallback/lib1/differentExtension.js
import "@/differentExtension.js" // $ importTarget=Fallback/lib2/differentExtension.ts SPURIOUS: importTarget=Fallback/lib1/differentExtension.js
import "@/subdir" // $ importTarget=Fallback/lib1/subdir/index.ts SPURIOUS: importTarget=Fallback/lib2/subdir/index.ts
import "@/subdir/both" // $ importTarget=Fallback/lib1/subdir/both.ts SPURIOUS: importTarget=Fallback/lib2/subdir/both.ts
import "@/subdir/only1" // $ importTarget=Fallback/lib1/subdir/only1.ts
import "@/subdir/only2" // $ importTarget=Fallback/lib2/subdir/only2.ts

View File

@@ -0,0 +1,8 @@
{
"include": ["."],
"compilerOptions": {
"paths": {
"@/*": ["./lib1/*", "./lib2/*"],
}
}
}

View File

@@ -0,0 +1,5 @@
/**
* @providesModule jsdoc-provided-module/foo/bar/baz
*/
export const x = 1;

View File

@@ -0,0 +1 @@
import 'jsdoc-provided-module/foo/bar/baz'; // $ importTarget=JSDocProvide/lib.js

Some files were not shown because too many files have changed in this diff Show More