diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 49b22ddcd2d4..22d008637c9a 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -404,7 +404,7 @@ private void setupFilters() { 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 diff --git a/javascript/extractor/src/com/semmle/js/extractor/JSONExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/JSONExtractor.java index c2cad57c3c96..1ca4eeb26096 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/JSONExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/JSONExtractor.java @@ -29,7 +29,7 @@ public Context(Label parent, int childIndex) { private final boolean tolerateParseErrors; public JSONExtractor(ExtractorConfig config) { - this.tolerateParseErrors = config.isTolerateParseErrors(); + this.tolerateParseErrors = true; } @Override diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 45691d1d7f42..59be61388e38 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -301,9 +301,14 @@ public void setupMatchers(ArgsParser ap) { // 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)) diff --git a/javascript/ql/examples/snippets/importfrom.ql b/javascript/ql/examples/snippets/importfrom.ql index d466136ca2f2..a2efb17e169f 100644 --- a/javascript/ql/examples/snippets/importfrom.ql +++ b/javascript/ql/examples/snippets/importfrom.ql @@ -11,5 +11,5 @@ import javascript from ImportDeclaration id -where id.getImportedPath().getValue() = "react" +where id.getImportedPathString() = "react" select id diff --git a/javascript/ql/lib/definitions.qll b/javascript/ql/lib/definitions.qll index 7b4806b14786..2cc9313d3132 100644 --- a/javascript/ql/lib/definitions.qll +++ b/javascript/ql/lib/definitions.qll @@ -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 diff --git a/javascript/ql/lib/semmle/javascript/AMD.qll b/javascript/ql/lib/semmle/javascript/AMD.qll index 3239dba9026d..4828aff27cc4 100644 --- a/javascript/ql/lib/semmle/javascript/AMD.qll +++ b/javascript/ql/lib/semmle/javascript/AMD.qll @@ -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 diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll index cc84fb87324d..379403eb0ee7 100644 --- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll @@ -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() } diff --git a/javascript/ql/lib/semmle/javascript/Expr.qll b/javascript/ql/lib/semmle/javascript/Expr.qll index e8ec55f0174d..8695c893f815 100644 --- a/javascript/ql/lib/semmle/javascript/Expr.qll +++ b/javascript/ql/lib/semmle/javascript/Expr.qll @@ -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()) } diff --git a/javascript/ql/lib/semmle/javascript/Files.qll b/javascript/ql/lib/semmle/javascript/Files.qll index 88513f087ae0..e717eb6def41 100644 --- a/javascript/ql/lib/semmle/javascript/Files.qll +++ b/javascript/ql/lib/semmle/javascript/Files.qll @@ -29,6 +29,8 @@ private module Impl = Make; 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() } } diff --git a/javascript/ql/lib/semmle/javascript/HTML.qll b/javascript/ql/lib/semmle/javascript/HTML.qll index 01ce54cef529..43b66db459ff 100644 --- a/javascript/ql/lib/semmle/javascript/HTML.qll +++ b/javascript/ql/lib/semmle/javascript/HTML.qll @@ -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; + /** * 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) } diff --git a/javascript/ql/lib/semmle/javascript/Modules.qll b/javascript/ql/lib/semmle/javascript/Modules.qll index 8c36044bb24d..3ddd5132d05d 100644 --- a/javascript/ql/lib/semmle/javascript/Modules.qll +++ b/javascript/ql/lib/semmle/javascript/Modules.qll @@ -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()) - } - - /** - * Gets a module with a `@providesModule` JSDoc tag that matches - * the imported path. - */ - private Module resolveAsProvidedModule() { - exists(JSDocTag tag | - tag.getTitle() = "providesModule" and - tag.getParent().getComment().getTopLevel() = result and - tag.getDescription().trim() = this.getImportedPath().getValue() - ) - } + Module resolveImportedPath() { result.getFile() = this.getImportedFile() } /** - * Gets a module in a `node_modules/@types/` folder that matches the imported module name. + * Gets the module the path of this import resolves to. */ - private Module resolveFromTypeRoot() { - result.getFile() = - min(TypeRootFolder typeRoot | - | - typeRoot.getModuleFile(this.getImportedPath().getValue()) - order by - typeRoot.getSearchPriority(this.getFile().getParentContainer()) - ) - } + File getImportedFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) } /** - * Gets the imported module, as determined by the TypeScript compiler, if any. + * DEPRECATED. Use `getImportedModule()` instead. */ - 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() - ) -} diff --git a/javascript/ql/lib/semmle/javascript/NPM.qll b/javascript/ql/lib/semmle/javascript/NPM.qll index b70ff8055b4f..92e349594144 100644 --- a/javascript/ql/lib/semmle/javascript/NPM.qll +++ b/javascript/ql/lib/semmle/javascript/NPM.qll @@ -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 diff --git a/javascript/ql/lib/semmle/javascript/NodeJS.qll b/javascript/ql/lib/semmle/javascript/NodeJS.qll index cd00e06e7226..7e9e2fdea904 100644 --- a/javascript/ql/lib/semmle/javascript/NodeJS.qll +++ b/javascript/ql/lib/semmle/javascript/NodeJS.qll @@ -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 `/node_modules` - * such that `` 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 ``. - * - * 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): - * - *
    - *
  • the file `c/p`; - *
  • the file `c/p.{tsx,ts,jsx,es6,es,mjs,cjs}`; - *
  • the file `c/p.js`; - *
  • the file `c/p.json`; - *
  • the file `c/p.node`; - *
  • if `c/p` is a folder: - *
      - *
    • if `c/p/package.json` exists and specifies a `main` module `m`: - *
        - *
      • the file `c/p/m`; - *
      • the file `c/p/m.{tsx,ts,jsx,es6,es,mjs,cjs}`; - *
      • the file `c/p/m.js`; - *
      • the file `c/p/m.json`; - *
      • the file `c/p/m.node`; - *
      - *
    • the file `c/p/index.{tsx,ts,jsx,es6,es,mjs,cjs}`; - *
    • the file `c/p/index.js`; - *
    • the file `c/p/index.json`; - *
    • the file `c/p/index.node`. - *
    - *
- * - * 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 diff --git a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll index 03b5bf93fb06..467e14aa74fb 100644 --- a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll +++ b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll @@ -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 } } diff --git a/javascript/ql/lib/semmle/javascript/PackageExports.qll b/javascript/ql/lib/semmle/javascript/PackageExports.qll index ace840613e1c..b4032980688f 100644 --- a/javascript/ql/lib/semmle/javascript/PackageExports.qll +++ b/javascript/ql/lib/semmle/javascript/PackageExports.qll @@ -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 diff --git a/javascript/ql/lib/semmle/javascript/Paths.qll b/javascript/ql/lib/semmle/javascript/Paths.qll index 66a840e9f26b..38c24a1b8e90 100644 --- a/javascript/ql/lib/semmle/javascript/Paths.qll +++ b/javascript/ql/lib/semmle/javascript/Paths.qll @@ -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 `/`. */ - 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 `/`. */ -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 diff --git a/javascript/ql/lib/semmle/javascript/TSConfig.qll b/javascript/ql/lib/semmle/javascript/TSConfig.qll new file mode 100644 index 000000000000..b38fa1d1dc92 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/TSConfig.qll @@ -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 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; + +/** + * 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() } +} diff --git a/javascript/ql/lib/semmle/javascript/TypeScript.qll b/javascript/ql/lib/semmle/javascript/TypeScript.qll index d8b6b63a366f..4be331ed6a50 100644 --- a/javascript/ql/lib/semmle/javascript/TypeScript.qll +++ b/javascript/ql/lib/semmle/javascript/TypeScript.qll @@ -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()) ) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll index 2e2313835227..7f9b375f534e 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll @@ -738,7 +738,7 @@ module ModuleImportNode { DefaultRange() { exists(Import i | this = i.getImportedModuleNode() and - i.getImportedPath().getValue() = path + i.getImportedPathString() = path ) or // AMD require diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll index bb18ff078466..3134d76d8f16 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll @@ -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) diff --git a/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll b/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll index 948b8c9aff20..8ca2add4a274 100644 --- a/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll +++ b/javascript/ql/lib/semmle/javascript/dependencies/Dependencies.qll @@ -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()) ) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll b/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll index 3441b5d9c006..9fd24c81a4a4 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll @@ -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; + /** * 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. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll index 1a6d11cd7534..a85a0a7813ce 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll @@ -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 | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll index 240e75627cf6..4b3f70b77725 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll @@ -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() + } + } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll b/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll index 15240ce5cc0d..2c460fcc3451 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LazyCache.qll @@ -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() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/React.qll b/javascript/ql/lib/semmle/javascript/frameworks/React.qll index 5ac0e419e264..4d126b888290 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/React.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/React.qll @@ -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()) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index faba601df52d..f571648294c2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -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() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index 1f51af3efda0..ee579d2c9db1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -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 diff --git a/javascript/ql/lib/semmle/javascript/internal/paths/JSPaths.qll b/javascript/ql/lib/semmle/javascript/internal/paths/JSPaths.qll new file mode 100644 index 000000000000..dc90dcc39469 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/paths/JSPaths.qll @@ -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 + ) + ) + } +} diff --git a/javascript/ql/lib/semmle/javascript/internal/paths/PackageJsonEx.qll b/javascript/ql/lib/semmle/javascript/internal/paths/PackageJsonEx.qll new file mode 100644 index 000000000000..4e2a51aaf7d4 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/paths/PackageJsonEx.qll @@ -0,0 +1,151 @@ +private import javascript +private import semmle.javascript.internal.paths.JSPaths + +/** + * Extension of `PackageJson` 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; + +/** + * 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() +} diff --git a/javascript/ql/lib/semmle/javascript/internal/paths/PathConcatenation.qll b/javascript/ql/lib/semmle/javascript/internal/paths/PathConcatenation.qll new file mode 100644 index 000000000000..448c7e55e016 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/paths/PathConcatenation.qll @@ -0,0 +1,35 @@ +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() { + this.getReceiver().(VarAccess).getName() = "path" and + this.getCalleeName() = ["join", "resolve"] + } + + override Expr getOperand(int n) { result = this.getArgument(n) } + + override string getSeparator() { result = "/" } +} diff --git a/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll b/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll new file mode 100644 index 000000000000..05b8a6207171 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/paths/PathExprResolver.qll @@ -0,0 +1,249 @@ +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 { + /** 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 + 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; + + 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; diff --git a/javascript/ql/lib/semmle/javascript/internal/paths/PathMapping.qll b/javascript/ql/lib/semmle/javascript/internal/paths/PathMapping.qll new file mode 100644 index 000000000000..d3da0a51cfa3 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/paths/PathMapping.qll @@ -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() } +} diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll index 7a6575f86475..f59c7b78e367 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll @@ -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 diff --git a/javascript/ql/src/Declarations/UnstableCyclicImport.ql b/javascript/ql/src/Declarations/UnstableCyclicImport.ql index 56f418c40a26..4ef4d8ca9465 100644 --- a/javascript/ql/src/Declarations/UnstableCyclicImport.ql +++ b/javascript/ql/src/Declarations/UnstableCyclicImport.ql @@ -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() diff --git a/javascript/ql/src/NodeJS/UnresolvableImport.ql b/javascript/ql/src/NodeJS/UnresolvableImport.ql index 16feba14348d..28c46229cf6a 100644 --- a/javascript/ql/src/NodeJS/UnresolvableImport.ql +++ b/javascript/ql/src/NodeJS/UnresolvableImport.ql @@ -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 diff --git a/javascript/ql/src/NodeJS/UnusedDependency.ql b/javascript/ql/src/NodeJS/UnusedDependency.ql index 45bbde37294d..438f589cac9a 100644 --- a/javascript/ql/src/NodeJS/UnusedDependency.ql +++ b/javascript/ql/src/NodeJS/UnusedDependency.ql @@ -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` diff --git a/javascript/ql/test/library-tests/AMD/tests.ql b/javascript/ql/test/library-tests/AMD/tests.ql index 956ad6d040ef..e71ae089f2d4 100644 --- a/javascript/ql/test/library-tests/AMD/tests.ql +++ b/javascript/ql/test/library-tests/AMD/tests.ql @@ -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() } diff --git a/javascript/ql/test/library-tests/AST/Decorators/printAst.expected b/javascript/ql/test/library-tests/AST/Decorators/printAst.expected index 5cbec5ec8830..1c426948b1d8 100644 --- a/javascript/ql/test/library-tests/AST/Decorators/printAst.expected +++ b/javascript/ql/test/library-tests/AST/Decorators/printAst.expected @@ -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 | diff --git a/javascript/ql/test/library-tests/Modules/tests.ql b/javascript/ql/test/library-tests/Modules/tests.ql index ca0e196f488c..efe72731a7ba 100644 --- a/javascript/ql/test/library-tests/Modules/tests.ql +++ b/javascript/ql/test/library-tests/Modules/tests.ql @@ -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) { diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/a/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/a/package.json new file mode 100644 index 000000000000..03b4185fbe2b --- /dev/null +++ b/javascript/ql/test/library-tests/NPM/src/node_modules/a/package.json @@ -0,0 +1,4 @@ +{ + "name": "a", + "main": "index.js" +} diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/nested/node_modules/a/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/nested/node_modules/a/package.json new file mode 100644 index 000000000000..5742f54f95c1 --- /dev/null +++ b/javascript/ql/test/library-tests/NPM/src/node_modules/nested/node_modules/a/package.json @@ -0,0 +1,5 @@ +{ + "name": "a", + "main": "index.js", + "description": "Nested version of package A" +} diff --git a/javascript/ql/test/library-tests/NPM/src/node_modules/nested/package.json b/javascript/ql/test/library-tests/NPM/src/node_modules/nested/package.json new file mode 100644 index 000000000000..9978c83c9fcc --- /dev/null +++ b/javascript/ql/test/library-tests/NPM/src/node_modules/nested/package.json @@ -0,0 +1,3 @@ +{ + "name": "nested" +} diff --git a/javascript/ql/test/library-tests/NPM/tests.expected b/javascript/ql/test/library-tests/NPM/tests.expected index a75c705be266..568bd0a7a821 100644 --- a/javascript/ql/test/library-tests/NPM/tests.expected +++ b/javascript/ql/test/library-tests/NPM/tests.expected @@ -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 | | | src/node_modules/nested/tst3.js:1:1:1:29 | require ... odule') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | | +| src/node_modules/nested/tst3.js:2:1:2:12 | require('a') | src/node_modules/a/index.js:1:1:1:25 | | | 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 | | | src/node_modules/tst2.js:1:1:1:38 | require ... cy.js') | src/node_modules/third-party-module/fancy.js:1:1:4:0 | | | src/test-submodule.js:1:1:1:24 | require ... odule") | src/node_modules/parent-module/main.js:1:1:2:0 | | @@ -29,10 +31,13 @@ modules | src | test-package | src/test-submodule.js:1:1:3:0 | | | src | test-package | src/tst2.js:1:1:1:13 | | | src | test-package | src/tst.js:1:1:2:38 | | +| src/node_modules/a | a | src/node_modules/a/index.js:1:1:1:25 | | | src/node_modules/b | b | src/node_modules/b/lib/index.js:1:1:2:0 | | | src/node_modules/b | b | src/node_modules/b/lib/util.ts:1:1:2:0 | | | src/node_modules/c | c | src/node_modules/c/src/index.js:1:1:2:0 | | | src/node_modules/d | d | src/node_modules/d/main.js:1:1:2:0 | | +| src/node_modules/nested | nested | src/node_modules/nested/tst3.js:1:1:2:13 | | +| src/node_modules/nested/node_modules/a | a | src/node_modules/nested/node_modules/a/index.js:1:1:1:25 | | | src/node_modules/parent-module | parent-module | src/node_modules/parent-module/main.js:1:1:2:0 | | | src/node_modules/parent-module | parent-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | | | src/node_modules/parent-module/sub-module | parent-module/sub-module | src/node_modules/parent-module/sub-module/main.js:1:1:2:0 | | @@ -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 | | | 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 | | | 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 | | | 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 | | +| 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 | | | 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 | | | 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 | | | 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 | | | src/package.json:1:1:20:1 | {\\n "na ... "\\n }\\n} | test-package | src/index.js:1:1:4:0 | | 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} | diff --git a/javascript/ql/test/library-tests/NodeJS/tests.expected b/javascript/ql/test/library-tests/NodeJS/tests.expected index b97c6a345e8f..964b784cf93f 100644 --- a/javascript/ql/test/library-tests/NodeJS/tests.expected +++ b/javascript/ql/test/library-tests/NodeJS/tests.expected @@ -94,14 +94,12 @@ requireImport | a.js:3:6:3:23 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | | a.js:4:6:4:29 | require ... /d.js') | ./sub/../d.js | d.js:1:1:7:15 | | | a.js:7:1:7:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | -| a.js:10:1:10:18 | require(__dirname) | | index.js:1:1:3:0 | | -| a.js:11:1:11:25 | require ... + '/e') | /e | e.js:1:1:6:0 | | | a.js:12:1:12:28 | require ... + 'c') | ./sub/c | sub/c.js:1:1:4:0 | | | b.js:1:1:1:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | | d.js:7:1:7:14 | require('foo') | foo | sub/f.js:1:1:4:17 | | -| index.js:2:1:2:41 | require ... b.js")) | /index.js/../b.js | b.js:1:1:8:0 | | | 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 | | | 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 | | +| 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 | | | 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 | | | reexport/b.js:1:11:1:24 | require("./a") | ./a | reexport/a.js:1:1:3:1 | | | sub/c.js:1:1:1:15 | require('../a') | ../a | a.js:1:1:14:0 | | diff --git a/javascript/ql/test/library-tests/NodeJS/tests.ql b/javascript/ql/test/library-tests/NodeJS/tests.ql index eca3a132d263..f5e0f58dc467 100644 --- a/javascript/ql/test/library-tests/NodeJS/tests.ql +++ b/javascript/ql/test/library-tests/NodeJS/tests.ql @@ -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() diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/.babelrc.json b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/.babelrc.json similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/.babelrc.json rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/.babelrc.json diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/a.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/a.js similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/a.js rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/a.js diff --git a/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/index.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/index.js new file mode 100644 index 000000000000..210dcc7893f6 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/index.js @@ -0,0 +1 @@ +import f from '~/a'; // $ importTarget=BabelRootImport/tst1/a.js diff --git a/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/nested/tst.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/nested/tst.js new file mode 100644 index 000000000000..210dcc7893f6 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst1/nested/tst.js @@ -0,0 +1 @@ +import f from '~/a'; // $ importTarget=BabelRootImport/tst1/a.js diff --git a/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/index.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/index.js new file mode 100644 index 000000000000..2c75396d24ea --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/index.js @@ -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 diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/package.json b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/package.json similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/package.json rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/package.json diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/src/js/b.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/src/js/b.js similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/src/js/b.js rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst2/src/js/b.js diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/.babelrc.json b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/.babelrc.json similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/.babelrc.json rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/.babelrc.json diff --git a/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/index.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/index.js new file mode 100644 index 000000000000..a4256149187a --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/index.js @@ -0,0 +1 @@ +import greeting from '~/b.js'; // $ importTarget=BabelRootImport/tst3/src/b.js diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/src/b.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/src/b.js similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/src/b.js rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst3/src/b.js diff --git a/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/index.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/index.js new file mode 100644 index 000000000000..7208e926bd9a --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/index.js @@ -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 diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/package.json b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/package.json similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/package.json rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/package.json diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/src/js/b.js b/javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/src/js/b.js similarity index 100% rename from javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/src/js/b.js rename to javascript/ql/test/library-tests/PathResolution/BabelRootImport/tst4/src/js/b.js diff --git a/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/file.ts b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/file.ts new file mode 100644 index 000000000000..f8a2b761a99e --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/file.ts @@ -0,0 +1 @@ +export const x = "file.ts" diff --git a/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/index.ts b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/index.ts new file mode 100644 index 000000000000..8d1382ced0ce --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/index.ts @@ -0,0 +1 @@ +export const x = "index.ts"; diff --git a/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/nostar.ts b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/nostar.ts new file mode 100644 index 000000000000..9245143d085d --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib/nostar.ts @@ -0,0 +1 @@ +export const x = "nostar.ts" diff --git a/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib2/file.ts b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib2/file.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BaseUrl/base/lib2/file.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/BaseUrl/src/main.ts b/javascript/ql/test/library-tests/PathResolution/BaseUrl/src/main.ts new file mode 100644 index 000000000000..9c5b90729035 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BaseUrl/src/main.ts @@ -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"; diff --git a/javascript/ql/test/library-tests/PathResolution/BaseUrl/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/BaseUrl/tsconfig.json new file mode 100644 index 000000000000..9167650c05a5 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/BaseUrl/tsconfig.json @@ -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"] + } + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/Basic/Subdir/sub.ts b/javascript/ql/test/library-tests/PathResolution/Basic/Subdir/sub.ts new file mode 100644 index 000000000000..375c5f955376 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Basic/Subdir/sub.ts @@ -0,0 +1,4 @@ +import '..'; // $ importTarget=Basic/index.ts +import '../'; // $ importTarget=Basic/index.ts +import './..'; // $ importTarget=Basic/index.ts +import './../'; // $ importTarget=Basic/index.ts diff --git a/javascript/ql/test/library-tests/PathResolution/Basic/index.ts b/javascript/ql/test/library-tests/PathResolution/Basic/index.ts new file mode 100644 index 000000000000..1d8ed2b606a6 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Basic/index.ts @@ -0,0 +1 @@ +import './Subdir/sub'; // $ importTarget=Basic/Subdir/sub.ts diff --git a/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/split.d.ts b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/split.d.ts new file mode 100644 index 000000000000..83e844e755e8 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/split.d.ts @@ -0,0 +1,5 @@ +// Type declaration for dual-module +export interface DualType { + prop: string; +} +export declare function helper(): DualType; diff --git a/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/split.js b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/split.js new file mode 100644 index 000000000000..be8d5d53bafe --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/split.js @@ -0,0 +1,4 @@ +// Implementation of dual-module +export function helper() { + return { prop: "implementation" }; +} diff --git a/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.d.ts b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.d.ts new file mode 100644 index 000000000000..226fa6a598be --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.d.ts @@ -0,0 +1 @@ +export const x: number; diff --git a/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.js b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.ts b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/lib/typescript.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/src/main.ts b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/src/main.ts new file mode 100644 index 000000000000..7682a39f00a0 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DeclarationFiles/src/main.ts @@ -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 diff --git a/javascript/ql/test/library-tests/PathResolution/DirnameImports/main.js b/javascript/ql/test/library-tests/PathResolution/DirnameImports/main.js new file mode 100644 index 000000000000..154f4e47f6a2 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DirnameImports/main.js @@ -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 diff --git a/javascript/ql/test/library-tests/PathResolution/DirnameImports/nested/target.js b/javascript/ql/test/library-tests/PathResolution/DirnameImports/nested/target.js new file mode 100644 index 000000000000..329f6242a404 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DirnameImports/nested/target.js @@ -0,0 +1,2 @@ +// Nested target file for __dirname imports +module.exports = { name: 'nested-target' }; diff --git a/javascript/ql/test/library-tests/PathResolution/DirnameImports/target.js b/javascript/ql/test/library-tests/PathResolution/DirnameImports/target.js new file mode 100644 index 000000000000..bb9a3de92023 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DirnameImports/target.js @@ -0,0 +1,2 @@ +// Target file for __dirname imports +module.exports = { name: 'target' }; diff --git a/javascript/ql/test/library-tests/PathResolution/DistContainsSrc/package.json b/javascript/ql/test/library-tests/PathResolution/DistContainsSrc/package.json new file mode 100644 index 000000000000..d830ef0f5679 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DistContainsSrc/package.json @@ -0,0 +1,3 @@ +{ + "main": "./dist/src/foo.js" +} diff --git a/javascript/ql/test/library-tests/PathResolution/DistContainsSrc/src/foo.ts b/javascript/ql/test/library-tests/PathResolution/DistContainsSrc/src/foo.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/DistContainsSrc/src/foo.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Extended/base/tsconfig.base.json b/javascript/ql/test/library-tests/PathResolution/Extended/base/tsconfig.base.json new file mode 100644 index 000000000000..dd5518cad9ec --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Extended/base/tsconfig.base.json @@ -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"] + } + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/Extended/lib/file.ts b/javascript/ql/test/library-tests/PathResolution/Extended/lib/file.ts new file mode 100644 index 000000000000..f8a2b761a99e --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Extended/lib/file.ts @@ -0,0 +1 @@ +export const x = "file.ts" diff --git a/javascript/ql/test/library-tests/PathResolution/Extended/lib/index.ts b/javascript/ql/test/library-tests/PathResolution/Extended/lib/index.ts new file mode 100644 index 000000000000..8d1382ced0ce --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Extended/lib/index.ts @@ -0,0 +1 @@ +export const x = "index.ts"; diff --git a/javascript/ql/test/library-tests/PathResolution/Extended/lib/nostar.ts b/javascript/ql/test/library-tests/PathResolution/Extended/lib/nostar.ts new file mode 100644 index 000000000000..9245143d085d --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Extended/lib/nostar.ts @@ -0,0 +1 @@ +export const x = "nostar.ts" diff --git a/javascript/ql/test/library-tests/PathResolution/Extended/src/main.ts b/javascript/ql/test/library-tests/PathResolution/Extended/src/main.ts new file mode 100644 index 000000000000..949bc7c7908c --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Extended/src/main.ts @@ -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"; diff --git a/javascript/ql/test/library-tests/PathResolution/Extended/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/Extended/tsconfig.json new file mode 100644 index 000000000000..608d152f71a0 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Extended/tsconfig.json @@ -0,0 +1,4 @@ +{ + "include": ["."], + "extends": "./base/tsconfig.base.json" +} diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/both.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/both.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/both.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/differentExtension.js b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/differentExtension.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/differentExtension.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/only1.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/only1.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/only1.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/both.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/both.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/both.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/index.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/index.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/index.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/only1.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/only1.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib1/subdir/only1.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/both.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/both.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/both.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/differentExtension.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/differentExtension.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/differentExtension.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/only2.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/only2.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/only2.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/both.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/both.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/both.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/index.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/index.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/index.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/only2.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/only2.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/lib2/subdir/only2.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/src/main.ts b/javascript/ql/test/library-tests/PathResolution/Fallback/src/main.ts new file mode 100644 index 000000000000..aad239811b53 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/src/main.ts @@ -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 diff --git a/javascript/ql/test/library-tests/PathResolution/Fallback/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/Fallback/tsconfig.json new file mode 100644 index 000000000000..239fac925588 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/Fallback/tsconfig.json @@ -0,0 +1,8 @@ +{ + "include": ["."], + "compilerOptions": { + "paths": { + "@/*": ["./lib1/*", "./lib2/*"], + } + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/JSDocProvide/lib.js b/javascript/ql/test/library-tests/PathResolution/JSDocProvide/lib.js new file mode 100644 index 000000000000..ba12858811b9 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/JSDocProvide/lib.js @@ -0,0 +1,5 @@ +/** + * @providesModule jsdoc-provided-module/foo/bar/baz + */ + +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/JSDocProvide/main.js b/javascript/ql/test/library-tests/PathResolution/JSDocProvide/main.js new file mode 100644 index 000000000000..5e1aaf8bd7b3 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/JSDocProvide/main.js @@ -0,0 +1 @@ +import 'jsdoc-provided-module/foo/bar/baz'; // $ importTarget=JSDocProvide/lib.js diff --git a/javascript/ql/test/library-tests/PathResolution/MainIsFolder/package.json b/javascript/ql/test/library-tests/PathResolution/MainIsFolder/package.json new file mode 100644 index 000000000000..d5947ffcfa5d --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/MainIsFolder/package.json @@ -0,0 +1,4 @@ +{ + "name": "@example/main-is-folder", + "main": "dist" +} diff --git a/javascript/ql/test/library-tests/PathResolution/MainIsFolder/src/index.ts b/javascript/ql/test/library-tests/PathResolution/MainIsFolder/src/index.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/MainIsFolder/src/index.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/MinifiedMain/package.json b/javascript/ql/test/library-tests/PathResolution/MinifiedMain/package.json new file mode 100644 index 000000000000..bd7204f0a66b --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/MinifiedMain/package.json @@ -0,0 +1,3 @@ +{ + "main": "dist/library.min.js" +} diff --git a/javascript/ql/test/library-tests/PathResolution/MinifiedMain/src/library.ts b/javascript/ql/test/library-tests/PathResolution/MinifiedMain/src/library.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/MinifiedMain/src/library.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/file.ts b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/file.ts new file mode 100644 index 000000000000..f8a2b761a99e --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/file.ts @@ -0,0 +1 @@ +export const x = "file.ts" diff --git a/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/index.ts b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/index.ts new file mode 100644 index 000000000000..8d1382ced0ce --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/index.ts @@ -0,0 +1 @@ +export const x = "index.ts"; diff --git a/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/nostar.ts b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/nostar.ts new file mode 100644 index 000000000000..9245143d085d --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/lib/nostar.ts @@ -0,0 +1 @@ +export const x = "nostar.ts" diff --git a/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/src/main.ts b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/src/main.ts new file mode 100644 index 000000000000..799794a2c6b4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/src/main.ts @@ -0,0 +1,35 @@ +// Relative import +import "../lib/file"; // $ importTarget=NoBaseUrl/lib/file.ts +import "../lib/file.ts"; // $ importTarget=NoBaseUrl/lib/file.ts +import "../lib/file.js"; // $ importTarget=NoBaseUrl/lib/file.ts +import "../lib"; // $ importTarget=NoBaseUrl/lib/index.ts +import "../lib/index"; // $ importTarget=NoBaseUrl/lib/index.ts +import "../lib/index.ts"; // $ importTarget=NoBaseUrl/lib/index.ts +import "../lib/index.js"; // $ importTarget=NoBaseUrl/lib/index.ts + +// Import unresolvable due to missing baseUrl +import "lib/file"; +import "lib/file.ts"; +import "lib/file.js"; +import "lib"; +import "lib/index"; +import "lib/index.ts"; +import "lib/index.js"; + +// Import matching "@/*" path mapping +import "@/file"; // $ importTarget=NoBaseUrl/lib/file.ts +import "@/file.ts"; // $ importTarget=NoBaseUrl/lib/file.ts +import "@/file.js"; // $ importTarget=NoBaseUrl/lib/file.ts +import "@"; // $ importTarget=NoBaseUrl/lib/nostar.ts +import "@/index"; // $ importTarget=NoBaseUrl/lib/index.ts +import "@/index.ts"; // $ importTarget=NoBaseUrl/lib/index.ts +import "@/index.js"; // $ importTarget=NoBaseUrl/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"; diff --git a/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/tsconfig.json new file mode 100644 index 000000000000..192347d919c4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NoBaseUrl/tsconfig.json @@ -0,0 +1,10 @@ +{ + "include": ["."], + "compilerOptions": { + "paths": { + "@/*": ["./lib/*"], + "@/*.xyz": ["./lib/*"], + "@": ["./lib/nostar.ts"] + } + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/NodeModules/node_modules/foo/index.js b/javascript/ql/test/library-tests/PathResolution/NodeModules/node_modules/foo/index.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NodeModules/node_modules/foo/index.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/NodeModules/node_modules/foo/package.json b/javascript/ql/test/library-tests/PathResolution/NodeModules/node_modules/foo/package.json new file mode 100644 index 000000000000..7173713a8cd4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NodeModules/node_modules/foo/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/javascript/ql/test/library-tests/PathResolution/NodeModules/outermain.ts b/javascript/ql/test/library-tests/PathResolution/NodeModules/outermain.ts new file mode 100644 index 000000000000..47da8ef42292 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NodeModules/outermain.ts @@ -0,0 +1,2 @@ +import 'foo'; // $ importTarget=NodeModules/node_modules/foo/index.js +import 'bar'; // $ SPURIOUS: importTarget=NodeModules/subfolder/node_modules/bar/index.js // Technically would not resolve diff --git a/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/node_modules/bar/index.js b/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/node_modules/bar/index.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/node_modules/bar/index.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/node_modules/bar/package.json b/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/node_modules/bar/package.json new file mode 100644 index 000000000000..f31168998ef3 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/node_modules/bar/package.json @@ -0,0 +1,3 @@ +{ + "name": "bar" +} diff --git a/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/src/main.ts b/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/src/main.ts new file mode 100644 index 000000000000..1da40b0e2f8c --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/NodeModules/subfolder/src/main.ts @@ -0,0 +1,2 @@ +import 'foo'; // $ importTarget=NodeModules/node_modules/foo/index.js +import 'bar'; // $ importTarget=NodeModules/subfolder/node_modules/bar/index.js diff --git a/javascript/ql/test/library-tests/PathResolution/PackageGuess1/package.json b/javascript/ql/test/library-tests/PathResolution/PackageGuess1/package.json new file mode 100644 index 000000000000..ea524ea79000 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageGuess1/package.json @@ -0,0 +1,3 @@ +{ + "name": "@example/package-guess1" +} diff --git a/javascript/ql/test/library-tests/PathResolution/PackageGuess1/src/index.ts b/javascript/ql/test/library-tests/PathResolution/PackageGuess1/src/index.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageGuess1/src/index.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageGuess2/blah/stuff.ts b/javascript/ql/test/library-tests/PathResolution/PackageGuess2/blah/stuff.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageGuess2/blah/stuff.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageGuess2/package.json b/javascript/ql/test/library-tests/PathResolution/PackageGuess2/package.json new file mode 100644 index 000000000000..afa0dbe1c8ed --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageGuess2/package.json @@ -0,0 +1,6 @@ +{ + "name": "@example/package-guess2", + "files": [ + "./blah/stuff.js" + ] +} diff --git a/javascript/ql/test/library-tests/PathResolution/PackageIndexFile/index.js b/javascript/ql/test/library-tests/PathResolution/PackageIndexFile/index.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageIndexFile/index.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageIndexFile/package.json b/javascript/ql/test/library-tests/PathResolution/PackageIndexFile/package.json new file mode 100644 index 000000000000..8d52b4ea6560 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageIndexFile/package.json @@ -0,0 +1,3 @@ +{ + "name": "@example/package-with-index-file" +} diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithExports/fake-file-impl.js b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/fake-file-impl.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/fake-file-impl.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithExports/main.js b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/main.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/main.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithExports/package.json b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/package.json new file mode 100644 index 000000000000..229803c3301b --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/package.json @@ -0,0 +1,14 @@ +{ + "name": "@example/package-with-exports", + "exports": { + ".": { + "default": "./main.js" + }, + "./fake-file": { + "default": "./fake-file-impl.js" + }, + "./star/*": { + "default": "./star-impl/*" + } + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithExports/star-impl/foo.js b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/star-impl/foo.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithExports/star-impl/foo.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithMain/main.js b/javascript/ql/test/library-tests/PathResolution/PackageWithMain/main.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithMain/main.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithMain/package.json b/javascript/ql/test/library-tests/PathResolution/PackageWithMain/package.json new file mode 100644 index 000000000000..7aab9ed43163 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithMain/package.json @@ -0,0 +1,4 @@ +{ + "name": "@example/package-with-main", + "main": "./main.js" +} diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithMainExt/lib/main.ts b/javascript/ql/test/library-tests/PathResolution/PackageWithMainExt/lib/main.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithMainExt/lib/main.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithMainExt/package.json b/javascript/ql/test/library-tests/PathResolution/PackageWithMainExt/package.json new file mode 100644 index 000000000000..c178bf7cbb2d --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithMainExt/package.json @@ -0,0 +1,4 @@ +{ + "name": "@example/package-with-main-ext", + "main": "./lib/main.js" +} diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithModuleMain/main.js b/javascript/ql/test/library-tests/PathResolution/PackageWithModuleMain/main.js new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithModuleMain/main.js @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/PackageWithModuleMain/package.json b/javascript/ql/test/library-tests/PathResolution/PackageWithModuleMain/package.json new file mode 100644 index 000000000000..ff43010d7d5b --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/PackageWithModuleMain/package.json @@ -0,0 +1,4 @@ +{ + "name": "@example/package-with-module-main", + "module": "./main.js" +} diff --git a/javascript/ql/test/library-tests/PathResolution/RootDir/my-root/foo.ts b/javascript/ql/test/library-tests/PathResolution/RootDir/my-root/foo.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/RootDir/my-root/foo.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/RootDir/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/RootDir/tsconfig.json new file mode 100644 index 000000000000..2bfc42007b62 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/RootDir/tsconfig.json @@ -0,0 +1,7 @@ +{ + "include": ["my-root","my-tests"], + "compilerOptions": { + "rootDir": "./my-root", + "outDir": "./my-out", + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/TSConfigOutDir/src/foo.ts b/javascript/ql/test/library-tests/PathResolution/TSConfigOutDir/src/foo.ts new file mode 100644 index 000000000000..ad1d380d6cc4 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/TSConfigOutDir/src/foo.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/javascript/ql/test/library-tests/PathResolution/TSConfigOutDir/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/TSConfigOutDir/tsconfig.json new file mode 100644 index 000000000000..09d14078cb69 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/TSConfigOutDir/tsconfig.json @@ -0,0 +1,7 @@ +{ + "include": ["src"], + "compilerOptions": { + // Use unusual name to test independently of naming heuristics + "outDir": "./customOutDir" + } +} diff --git a/javascript/ql/test/library-tests/PathResolution/import-packages.ts b/javascript/ql/test/library-tests/PathResolution/import-packages.ts new file mode 100644 index 000000000000..6b7ca9014e4f --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/import-packages.ts @@ -0,0 +1,35 @@ +import './PackageWithMain/main'; // $ importTarget=PackageWithMain/main.js +import '@example/package-with-main'; // $ importTarget=PackageWithMain/main.js + +import './PackageWithModuleMain'; // $ importTarget=PackageWithModuleMain/main.js +import '@example/package-with-module-main'; // $ importTarget=PackageWithModuleMain/main.js + +import './PackageWithExports'; // Not a valid import +import './PackageWithExports/fake-file'; // Not a valid import +import './PackageWithExports/star/foo'; // Not a valid import +import '@example/package-with-exports'; // $ importTarget=PackageWithExports/main.js +import '@example/package-with-exports/fake-file'; // $ importTarget=PackageWithExports/fake-file-impl.js +import '@example/package-with-exports/star/foo'; // $ importTarget=PackageWithExports/star-impl/foo.js + +import './PackageIndexFile'; // $ importTarget=PackageIndexFile/index.js +import '@example/package-with-index-file'; // $ importTarget=PackageIndexFile/index.js + +import './PackageGuess1'; // $ importTarget=PackageGuess1/src/index.ts +import '@example/package-guess1'; // $ importTarget=PackageGuess1/src/index.ts + +import './PackageGuess2'; // $ importTarget=PackageGuess2/blah/stuff.ts +import '@example/package-guess2'; // $ importTarget=PackageGuess2/blah/stuff.ts + +import './PackageWithMainExt'; // $ importTarget=PackageWithMainExt/lib/main.ts +import '@example/package-with-main-ext'; // $ importTarget=PackageWithMainExt/lib/main.ts + +import './TSConfigOutDir/customOutDir/foo.js'; // $ importTarget=TSConfigOutDir/src/foo.ts + +import './MainIsFolder'; // $ importTarget=MainIsFolder/src/index.ts +import '@example/main-is-folder'; // $ importTarget=MainIsFolder/src/index.ts + +import './DistContainsSrc'; // $ importTarget=DistContainsSrc/src/foo.ts + +import './MinifiedMain'; // $ importTarget=MinifiedMain/src/library.ts + +import './RootDir/my-out/foo.js'; // $ importTarget=RootDir/my-root/foo.ts diff --git a/javascript/ql/test/library-tests/PathResolution/test.expected b/javascript/ql/test/library-tests/PathResolution/test.expected new file mode 100644 index 000000000000..4e1d2506e618 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/test.expected @@ -0,0 +1,132 @@ +| BabelRootImport/tst1/index.js:1:1:1:20 | import f from '~/a'; | BabelRootImport/tst1/a.js | +| BabelRootImport/tst1/nested/tst.js:1:1:1:20 | import f from '~/a'; | BabelRootImport/tst1/a.js | +| BabelRootImport/tst2/index.js:1:1:1:23 | import ... /b.js'; | BabelRootImport/tst2/src/js/b.js | +| BabelRootImport/tst2/index.js:2:1:2:20 | import f from '#/a'; | BabelRootImport/tst1/a.js | +| BabelRootImport/tst3/index.js:1:1:1:30 | import ... /b.js'; | BabelRootImport/tst3/src/b.js | +| BabelRootImport/tst4/index.js:1:1:1:23 | import ... /b.js'; | BabelRootImport/tst4/src/js/b.js | +| BabelRootImport/tst4/index.js:2:1:2:20 | import f from '#/a'; | BabelRootImport/tst1/a.js | +| BaseUrl/src/main.ts:2:1:2:26 | import ... /file"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:3:1:3:29 | import ... le.ts"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:4:1:4:29 | import ... le.js"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:5:1:5:21 | import ... e/lib"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:6:1:6:27 | import ... index"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:7:1:7:30 | import ... ex.ts"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:8:1:8:30 | import ... ex.js"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:11:1:11:18 | import "lib/file"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:12:1:12:21 | import ... le.ts"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:13:1:13:21 | import ... le.js"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:14:1:14:13 | import "lib"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:15:1:15:19 | import "lib/index"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:16:1:16:22 | import ... ex.ts"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:17:1:17:22 | import ... ex.js"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:20:1:20:16 | import "@/file"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:21:1:21:19 | import "@/file.ts"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:22:1:22:19 | import "@/file.js"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:23:1:23:11 | import "@"; | BaseUrl/base/lib/nostar.ts | +| BaseUrl/src/main.ts:24:1:24:17 | import "@/index"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:25:1:25:20 | import "@/index.ts"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:26:1:26:20 | import "@/index.js"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:29:1:29:16 | import "#/file"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:30:1:30:19 | import "#/file.ts"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:31:1:31:19 | import "#/file.js"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:32:1:32:17 | import "#/index"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:33:1:33:20 | import "#/index.ts"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:34:1:34:20 | import "#/index.js"; | BaseUrl/base/lib/index.ts | +| BaseUrl/src/main.ts:37:1:37:19 | import "^lib/file"; | BaseUrl/base/lib/file.ts | +| BaseUrl/src/main.ts:38:1:38:20 | import "^lib2/file"; | BaseUrl/base/lib2/file.ts | +| Basic/Subdir/sub.ts:1:1:1:12 | import '..'; | Basic/index.ts | +| Basic/Subdir/sub.ts:2:1:2:13 | import '../'; | Basic/index.ts | +| Basic/Subdir/sub.ts:3:1:3:14 | import './..'; | Basic/index.ts | +| Basic/Subdir/sub.ts:4:1:4:15 | import './../'; | Basic/index.ts | +| Basic/index.ts:1:1:1:22 | import ... r/sub'; | Basic/Subdir/sub.ts | +| DeclarationFiles/src/main.ts:1:1:1:22 | import ... split"; | DeclarationFiles/lib/split.d.ts | +| DeclarationFiles/src/main.ts:1:1:1:22 | import ... split"; | DeclarationFiles/lib/split.js | +| DeclarationFiles/src/main.ts:2:1:2:25 | import ... it.js"; | DeclarationFiles/lib/split.d.ts | +| DeclarationFiles/src/main.ts:2:1:2:25 | import ... it.js"; | DeclarationFiles/lib/split.js | +| DeclarationFiles/src/main.ts:3:1:3:27 | import ... .d.ts"; | DeclarationFiles/lib/split.d.ts | +| DeclarationFiles/src/main.ts:5:1:5:27 | import ... cript"; | DeclarationFiles/lib/typescript.ts | +| DeclarationFiles/src/main.ts:6:1:6:30 | import ... pt.js"; | DeclarationFiles/lib/typescript.ts | +| DeclarationFiles/src/main.ts:7:1:7:32 | import ... .d.ts"; | DeclarationFiles/lib/typescript.d.ts | +| DirnameImports/main.js:4:1:4:33 | require ... et.js') | DirnameImports/target.js | +| DirnameImports/main.js:5:1:5:40 | require ... et.js') | DirnameImports/nested/target.js | +| DirnameImports/main.js:6:1:6:45 | require ... es.ts') | import-packages.ts | +| DirnameImports/main.js:7:1:7:38 | require ... et.js') | DirnameImports/target.js | +| DirnameImports/main.js:9:1:9:42 | require ... t.js')) | DirnameImports/target.js | +| DirnameImports/main.js:10:1:10:45 | require ... t.js')) | DirnameImports/target.js | +| DirnameImports/main.js:13:1:13:48 | require ... et.js') | DirnameImports/nested/target.js | +| DirnameImports/main.js:15:1:15:33 | require ... et.js`) | DirnameImports/target.js | +| Extended/src/main.ts:2:1:2:21 | import ... /file"; | Extended/lib/file.ts | +| Extended/src/main.ts:3:1:3:24 | import ... le.ts"; | Extended/lib/file.ts | +| Extended/src/main.ts:4:1:4:24 | import ... le.js"; | Extended/lib/file.ts | +| Extended/src/main.ts:5:1:5:16 | import "../lib"; | Extended/lib/index.ts | +| Extended/src/main.ts:6:1:6:22 | import ... index"; | Extended/lib/index.ts | +| Extended/src/main.ts:7:1:7:25 | import ... ex.ts"; | Extended/lib/index.ts | +| Extended/src/main.ts:8:1:8:25 | import ... ex.js"; | Extended/lib/index.ts | +| Extended/src/main.ts:11:1:11:18 | import "lib/file"; | Extended/lib/file.ts | +| Extended/src/main.ts:12:1:12:21 | import ... le.ts"; | Extended/lib/file.ts | +| Extended/src/main.ts:13:1:13:21 | import ... le.js"; | Extended/lib/file.ts | +| Extended/src/main.ts:14:1:14:13 | import "lib"; | Extended/lib/index.ts | +| Extended/src/main.ts:15:1:15:19 | import "lib/index"; | Extended/lib/index.ts | +| Extended/src/main.ts:16:1:16:22 | import ... ex.ts"; | Extended/lib/index.ts | +| Extended/src/main.ts:17:1:17:22 | import ... ex.js"; | Extended/lib/index.ts | +| Extended/src/main.ts:20:1:20:16 | import "@/file"; | Extended/lib/file.ts | +| Extended/src/main.ts:21:1:21:19 | import "@/file.ts"; | Extended/lib/file.ts | +| Extended/src/main.ts:22:1:22:19 | import "@/file.js"; | Extended/lib/file.ts | +| Extended/src/main.ts:23:1:23:11 | import "@"; | Extended/lib/nostar.ts | +| Extended/src/main.ts:24:1:24:17 | import "@/index"; | Extended/lib/index.ts | +| Extended/src/main.ts:25:1:25:20 | import "@/index.ts"; | Extended/lib/index.ts | +| Extended/src/main.ts:26:1:26:20 | import "@/index.js"; | Extended/lib/index.ts | +| Fallback/src/main.ts:1:1:1:15 | import "@/both" | Fallback/lib1/both.ts | +| Fallback/src/main.ts:1:1:1:15 | import "@/both" | Fallback/lib2/both.ts | +| Fallback/src/main.ts:2:1:2:16 | import "@/only1" | Fallback/lib1/only1.ts | +| Fallback/src/main.ts:3:1:3:16 | import "@/only2" | Fallback/lib2/only2.ts | +| Fallback/src/main.ts:4:1:4:29 | import ... ension" | Fallback/lib1/differentExtension.js | +| Fallback/src/main.ts:4:1:4:29 | import ... ension" | Fallback/lib2/differentExtension.ts | +| Fallback/src/main.ts:5:1:5:32 | import ... ion.js" | Fallback/lib1/differentExtension.js | +| Fallback/src/main.ts:5:1:5:32 | import ... ion.js" | Fallback/lib2/differentExtension.ts | +| Fallback/src/main.ts:7:1:7:17 | import "@/subdir" | Fallback/lib1/subdir/index.ts | +| Fallback/src/main.ts:7:1:7:17 | import "@/subdir" | Fallback/lib2/subdir/index.ts | +| Fallback/src/main.ts:8:1:8:22 | import ... r/both" | Fallback/lib1/subdir/both.ts | +| Fallback/src/main.ts:8:1:8:22 | import ... r/both" | Fallback/lib2/subdir/both.ts | +| Fallback/src/main.ts:9:1:9:23 | import ... /only1" | Fallback/lib1/subdir/only1.ts | +| Fallback/src/main.ts:10:1:10:23 | import ... /only2" | Fallback/lib2/subdir/only2.ts | +| JSDocProvide/main.js:1:1:1:43 | import ... r/baz'; | JSDocProvide/lib.js | +| NoBaseUrl/src/main.ts:2:1:2:21 | import ... /file"; | NoBaseUrl/lib/file.ts | +| NoBaseUrl/src/main.ts:3:1:3:24 | import ... le.ts"; | NoBaseUrl/lib/file.ts | +| NoBaseUrl/src/main.ts:4:1:4:24 | import ... le.js"; | NoBaseUrl/lib/file.ts | +| NoBaseUrl/src/main.ts:5:1:5:16 | import "../lib"; | NoBaseUrl/lib/index.ts | +| NoBaseUrl/src/main.ts:6:1:6:22 | import ... index"; | NoBaseUrl/lib/index.ts | +| NoBaseUrl/src/main.ts:7:1:7:25 | import ... ex.ts"; | NoBaseUrl/lib/index.ts | +| NoBaseUrl/src/main.ts:8:1:8:25 | import ... ex.js"; | NoBaseUrl/lib/index.ts | +| NoBaseUrl/src/main.ts:20:1:20:16 | import "@/file"; | NoBaseUrl/lib/file.ts | +| NoBaseUrl/src/main.ts:21:1:21:19 | import "@/file.ts"; | NoBaseUrl/lib/file.ts | +| NoBaseUrl/src/main.ts:22:1:22:19 | import "@/file.js"; | NoBaseUrl/lib/file.ts | +| NoBaseUrl/src/main.ts:23:1:23:11 | import "@"; | NoBaseUrl/lib/nostar.ts | +| NoBaseUrl/src/main.ts:24:1:24:17 | import "@/index"; | NoBaseUrl/lib/index.ts | +| NoBaseUrl/src/main.ts:25:1:25:20 | import "@/index.ts"; | NoBaseUrl/lib/index.ts | +| NoBaseUrl/src/main.ts:26:1:26:20 | import "@/index.js"; | NoBaseUrl/lib/index.ts | +| NodeModules/outermain.ts:1:1:1:13 | import 'foo'; | NodeModules/node_modules/foo/index.js | +| NodeModules/outermain.ts:2:1:2:13 | import 'bar'; | NodeModules/subfolder/node_modules/bar/index.js | +| NodeModules/subfolder/src/main.ts:1:1:1:13 | import 'foo'; | NodeModules/node_modules/foo/index.js | +| NodeModules/subfolder/src/main.ts:2:1:2:13 | import 'bar'; | NodeModules/subfolder/node_modules/bar/index.js | +| import-packages.ts:1:1:1:32 | import ... /main'; | PackageWithMain/main.js | +| import-packages.ts:2:1:2:36 | import ... -main'; | PackageWithMain/main.js | +| import-packages.ts:4:1:4:33 | import ... eMain'; | PackageWithModuleMain/main.js | +| import-packages.ts:5:1:5:43 | import ... -main'; | PackageWithModuleMain/main.js | +| import-packages.ts:10:1:10:39 | import ... ports'; | PackageWithExports/main.js | +| import-packages.ts:11:1:11:49 | import ... -file'; | PackageWithExports/fake-file-impl.js | +| import-packages.ts:12:1:12:48 | import ... r/foo'; | PackageWithExports/star-impl/foo.js | +| import-packages.ts:14:1:14:28 | import ... xFile'; | PackageIndexFile/index.js | +| import-packages.ts:15:1:15:42 | import ... -file'; | PackageIndexFile/index.js | +| import-packages.ts:17:1:17:25 | import ... uess1'; | PackageGuess1/src/index.ts | +| import-packages.ts:18:1:18:33 | import ... uess1'; | PackageGuess1/src/index.ts | +| import-packages.ts:20:1:20:25 | import ... uess2'; | PackageGuess2/blah/stuff.ts | +| import-packages.ts:21:1:21:33 | import ... uess2'; | PackageGuess2/blah/stuff.ts | +| import-packages.ts:23:1:23:30 | import ... inExt'; | PackageWithMainExt/lib/main.ts | +| import-packages.ts:24:1:24:40 | import ... n-ext'; | PackageWithMainExt/lib/main.ts | +| import-packages.ts:26:1:26:46 | import ... oo.js'; | TSConfigOutDir/src/foo.ts | +| import-packages.ts:28:1:28:24 | import ... older'; | MainIsFolder/src/index.ts | +| import-packages.ts:29:1:29:33 | import ... older'; | MainIsFolder/src/index.ts | +| import-packages.ts:31:1:31:27 | import ... nsSrc'; | DistContainsSrc/src/foo.ts | +| import-packages.ts:33:1:33:24 | import ... dMain'; | MinifiedMain/src/library.ts | +| import-packages.ts:35:1:35:33 | import ... oo.js'; | RootDir/my-root/foo.ts | diff --git a/javascript/ql/test/library-tests/PathResolution/test.ql b/javascript/ql/test/library-tests/PathResolution/test.ql new file mode 100644 index 000000000000..a3cf0884659a --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/test.ql @@ -0,0 +1,5 @@ +import javascript + +query predicate importTarget(Import imprt, string value) { + imprt.getImportedModule().getFile().getRelativePath() = value +} diff --git a/javascript/ql/test/library-tests/PathResolution/test.qlref b/javascript/ql/test/library-tests/PathResolution/test.qlref new file mode 100644 index 000000000000..ab6773f15f90 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/test.qlref @@ -0,0 +1,2 @@ +query: test.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/library-tests/PathResolution/tsconfig.json b/javascript/ql/test/library-tests/PathResolution/tsconfig.json new file mode 100644 index 000000000000..81edd78b2ff8 --- /dev/null +++ b/javascript/ql/test/library-tests/PathResolution/tsconfig.json @@ -0,0 +1 @@ +{} // dummy file to enable full TS extraction diff --git a/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql b/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql index 839ba2c560bf..5de6c99c748d 100644 --- a/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql +++ b/javascript/ql/test/library-tests/TypeScript/ImportEquals/tests.ql @@ -5,7 +5,7 @@ query predicate dataFlowModuleImports(string name, DataFlow::SourceNode imp) { } query predicate imports(Import imprt, string path, Module mod) { - path = imprt.getImportedPath().getValue() and + path = imprt.getImportedPathString() and mod = imprt.getImportedModule() } diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.expected index fdbbf878689a..d8bf886588a6 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.expected @@ -1,2 +1,3 @@ | main.ts:1:1:1:52 | import ... -file"; | ./only-declaration-file | only-declaration-file.d.ts:3:1:4:0 | | +| main.ts:2:1:2:48 | import ... -file"; | ./has-javascript-file | has-javascript-file.d.ts:3:1:4:0 | | | main.ts:2:1:2:48 | import ... -file"; | ./has-javascript-file | has-javascript-file.js:1:1:2:0 | | diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql index 3c5e8a7fd168..0c8a95e3b3bd 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportDtsFile/test.ql @@ -1,4 +1,4 @@ import javascript from Import imprt -select imprt, imprt.getImportedPath().getValue(), imprt.getImportedModule() +select imprt, imprt.getImportedPathString(), imprt.getImportedModule() diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/MissingTypeRoot/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/MissingTypeRoot/test.expected index 51c6bdf27ff0..5f7ccb159cd4 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/MissingTypeRoot/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/MissingTypeRoot/test.expected @@ -1 +1,2 @@ +| tsconfig.json:0:0:0:0 | tsconfig.json | | tst.ts:0:0:0:0 | tst.ts | diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TypeRootFile/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TypeRootFile/test.expected index fc88cb47c18d..840934a14d38 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TypeRootFile/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TypeRootFile/test.expected @@ -1,2 +1,3 @@ +| tsconfig.json:0:0:0:0 | tsconfig.json | | tst.ts:0:0:0:0 | tst.ts | | typeroot.d.ts:0:0:0:0 | typeroot.d.ts | diff --git a/javascript/ql/test/library-tests/TypeScript/TSConfigReferences/test.expected b/javascript/ql/test/library-tests/TypeScript/TSConfigReferences/test.expected index 18dbed450ba1..47ea4115ae61 100644 --- a/javascript/ql/test/library-tests/TypeScript/TSConfigReferences/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/TSConfigReferences/test.expected @@ -7,3 +7,5 @@ types | string | | void | jsonFiles +| tsconfig.foo.json:0:0:0:0 | tsconfig.foo.json | +| tsconfig.json:0:0:0:0 | tsconfig.json | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index 04d6e6247ff0..3860acf0afb3 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -206,50 +206,62 @@ nodes | middle-rest.ts:3:8:3:11 | [Literal] true | semmle.label | [Literal] true | | middle-rest.ts:3:14:3:20 | [Literal] "hello" | semmle.label | [Literal] "hello" | | middle-rest.ts:3:23:3:25 | [Literal] 123 | semmle.label | [Literal] 123 | +| tsconfig.json:1:1:9:1 | [JsonObject] {compilerOptions: ...} | semmle.label | [JsonObject] {compilerOptions: ...} | +| tsconfig.json:1:1:9:1 | [JsonObject] {compilerOptions: ...} | semmle.order | 18 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | semmle.label | [JsonObject] {module: ...} | +| tsconfig.json:3:15:3:22 | [JsonString] "esnext" | semmle.label | [JsonString] "esnext" | +| tsconfig.json:4:15:4:22 | [JsonString] "esnext" | semmle.label | [JsonString] "esnext" | +| tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | semmle.label | [JsonArray] ["dom", ...] | +| tsconfig.json:5:13:5:17 | [JsonString] "dom" | semmle.label | [JsonString] "dom" | +| tsconfig.json:5:20:5:27 | [JsonString] "esnext" | semmle.label | [JsonString] "esnext" | +| tsconfig.json:6:26:6:29 | [JsonBoolean] true | semmle.label | [JsonBoolean] true | +| tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | semmle.label | [JsonArray] [".ios", ...] | +| tsconfig.json:7:24:7:29 | [JsonString] ".ios" | semmle.label | [JsonString] ".ios" | +| tsconfig.json:7:32:7:33 | [JsonString] "" | semmle.label | [JsonString] "" | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 18 | +| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 19 | | tst.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | tst.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | tst.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.label | [DeclStmt] var numVar = ... | -| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 19 | +| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 20 | | tst.ts:3:5:3:10 | [VarDecl] numVar | semmle.label | [VarDecl] numVar | | tst.ts:3:5:3:18 | [VariableDeclarator] numVar: number | semmle.label | [VariableDeclarator] numVar: number | | tst.ts:3:13:3:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.label | [DeclStmt] var num1 = ... | -| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 20 | +| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 21 | | tst.ts:5:5:5:8 | [VarDecl] num1 | semmle.label | [VarDecl] num1 | | tst.ts:5:5:5:17 | [VariableDeclarator] num1 = numVar | semmle.label | [VariableDeclarator] num1 = numVar | | tst.ts:5:12:5:17 | [VarRef] numVar | semmle.label | [VarRef] numVar | | tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.label | [DeclStmt] var num2 = ... | -| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 21 | +| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 22 | | tst.ts:6:5:6:8 | [VarDecl] num2 | semmle.label | [VarDecl] num2 | | tst.ts:6:5:6:12 | [VariableDeclarator] num2 = 5 | semmle.label | [VariableDeclarator] num2 = 5 | | tst.ts:6:12:6:12 | [Literal] 5 | semmle.label | [Literal] 5 | | tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.label | [DeclStmt] var num3 = ... | -| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 22 | +| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 23 | | tst.ts:7:5:7:8 | [VarDecl] num3 | semmle.label | [VarDecl] num3 | | tst.ts:7:5:7:22 | [VariableDeclarator] num3 = num1 + num2 | semmle.label | [VariableDeclarator] num3 = num1 + num2 | | tst.ts:7:12:7:15 | [VarRef] num1 | semmle.label | [VarRef] num1 | | tst.ts:7:12:7:22 | [BinaryExpr] num1 + num2 | semmle.label | [BinaryExpr] num1 + num2 | | tst.ts:7:19:7:22 | [VarRef] num2 | semmle.label | [VarRef] num2 | | tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.label | [DeclStmt] var strVar = ... | -| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 23 | +| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 24 | | tst.ts:9:5:9:10 | [VarDecl] strVar | semmle.label | [VarDecl] strVar | | tst.ts:9:5:9:18 | [VariableDeclarator] strVar: string | semmle.label | [VariableDeclarator] strVar: string | | tst.ts:9:13:9:18 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.label | [DeclStmt] var hello = ... | -| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 24 | +| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 25 | | tst.ts:10:5:10:9 | [VarDecl] hello | semmle.label | [VarDecl] hello | | tst.ts:10:5:10:19 | [VariableDeclarator] hello = "hello" | semmle.label | [VariableDeclarator] hello = "hello" | | tst.ts:10:13:10:19 | [Literal] "hello" | semmle.label | [Literal] "hello" | | tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.label | [DeclStmt] var world = ... | -| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 25 | +| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 26 | | tst.ts:11:5:11:9 | [VarDecl] world | semmle.label | [VarDecl] world | | tst.ts:11:5:11:19 | [VariableDeclarator] world = "world" | semmle.label | [VariableDeclarator] world = "world" | | tst.ts:11:13:11:19 | [Literal] "world" | semmle.label | [Literal] "world" | | tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.label | [DeclStmt] var msg = ... | -| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 26 | +| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 27 | | tst.ts:12:5:12:7 | [VarDecl] msg | semmle.label | [VarDecl] msg | | tst.ts:12:5:12:29 | [VariableDeclarator] msg = h ... + world | semmle.label | [VariableDeclarator] msg = h ... + world | | tst.ts:12:11:12:15 | [VarRef] hello | semmle.label | [VarRef] hello | @@ -258,7 +270,7 @@ nodes | tst.ts:12:19:12:21 | [Literal] " " | semmle.label | [Literal] " " | | tst.ts:12:25:12:29 | [VarRef] world | semmle.label | [VarRef] world | | tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | +| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 28 | | tst.ts:14:10:14:15 | [VarDecl] concat | semmle.label | [VarDecl] concat | | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:14:20:14:25 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -271,7 +283,7 @@ nodes | tst.ts:14:56:14:60 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:14:60:14:60 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 28 | +| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 29 | | tst.ts:16:10:16:12 | [VarDecl] add | semmle.label | [VarDecl] add | | tst.ts:16:14:16:14 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:16:17:16:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | @@ -284,7 +296,7 @@ nodes | tst.ts:16:53:16:57 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:16:57:16:57 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 29 | +| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 30 | | tst.ts:18:10:18:16 | [VarDecl] untyped | semmle.label | [VarDecl] untyped | | tst.ts:18:18:18:18 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:18:21:18:21 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -294,7 +306,7 @@ nodes | tst.ts:18:33:18:37 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:18:37:18:37 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 30 | +| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 31 | | tst.ts:20:10:20:21 | [VarDecl] partialTyped | semmle.label | [VarDecl] partialTyped | | tst.ts:20:23:20:23 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -305,7 +317,7 @@ nodes | tst.ts:20:46:20:50 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:20:50:20:50 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.label | [ForOfStmt] for (le ... 2]) {} | -| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 31 | +| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 32 | | tst.ts:22:6:22:20 | [DeclStmt] let numFromLoop = ... | semmle.label | [DeclStmt] let numFromLoop = ... | | tst.ts:22:10:22:20 | [VarDecl] numFromLoop | semmle.label | [VarDecl] numFromLoop | | tst.ts:22:10:22:20 | [VariableDeclarator] numFromLoop | semmle.label | [VariableDeclarator] numFromLoop | @@ -314,54 +326,54 @@ nodes | tst.ts:22:29:22:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:22:33:22:34 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.label | [DeclStmt] let array = ... | -| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 32 | +| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 33 | | tst.ts:24:5:24:9 | [VarDecl] array | semmle.label | [VarDecl] array | | tst.ts:24:5:24:19 | [VariableDeclarator] array: number[] | semmle.label | [VariableDeclarator] array: number[] | | tst.ts:24:12:24:17 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:24:12:24:19 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.label | [DeclStmt] let voidType = ... | -| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 33 | +| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 34 | | tst.ts:26:5:26:12 | [VarDecl] voidType | semmle.label | [VarDecl] voidType | | tst.ts:26:5:26:24 | [VariableDeclarator] voidType: () => void | semmle.label | [VariableDeclarator] voidType: () => void | | tst.ts:26:15:26:24 | [FunctionExpr] () => void | semmle.label | [FunctionExpr] () => void | | tst.ts:26:15:26:24 | [FunctionTypeExpr] () => void | semmle.label | [FunctionTypeExpr] () => void | | tst.ts:26:21:26:24 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | | tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.label | [DeclStmt] let undefinedType = ... | -| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 34 | +| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 35 | | tst.ts:27:5:27:17 | [VarDecl] undefinedType | semmle.label | [VarDecl] undefinedType | | tst.ts:27:5:27:28 | [VariableDeclarator] undefin ... defined | semmle.label | [VariableDeclarator] undefin ... defined | | tst.ts:27:20:27:28 | [KeywordTypeExpr] undefined | semmle.label | [KeywordTypeExpr] undefined | | tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.label | [DeclStmt] let nullType = ... | -| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 35 | +| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 36 | | tst.ts:28:5:28:12 | [VarDecl] nullType | semmle.label | [VarDecl] nullType | | tst.ts:28:5:28:25 | [VariableDeclarator] nullTyp ... = null | semmle.label | [VariableDeclarator] nullTyp ... = null | | tst.ts:28:15:28:18 | [KeywordTypeExpr] null | semmle.label | [KeywordTypeExpr] null | | tst.ts:28:22:28:25 | [Literal] null | semmle.label | [Literal] null | | tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.label | [DeclStmt] let neverType = ... | -| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 36 | +| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 37 | | tst.ts:29:5:29:13 | [VarDecl] neverType | semmle.label | [VarDecl] neverType | | tst.ts:29:5:29:26 | [VariableDeclarator] neverTy ... > never | semmle.label | [VariableDeclarator] neverTy ... > never | | tst.ts:29:16:29:26 | [FunctionExpr] () => never | semmle.label | [FunctionExpr] () => never | | tst.ts:29:16:29:26 | [FunctionTypeExpr] () => never | semmle.label | [FunctionTypeExpr] () => never | | tst.ts:29:22:29:26 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | | tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.label | [DeclStmt] let symbolType = ... | -| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 37 | +| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 38 | | tst.ts:30:5:30:14 | [VarDecl] symbolType | semmle.label | [VarDecl] symbolType | | tst.ts:30:5:30:22 | [VariableDeclarator] symbolType: symbol | semmle.label | [VariableDeclarator] symbolType: symbol | | tst.ts:30:17:30:22 | [KeywordTypeExpr] symbol | semmle.label | [KeywordTypeExpr] symbol | | tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.label | [DeclStmt] const uniqueSymbolType = ... | -| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 38 | +| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 39 | | tst.ts:31:7:31:22 | [VarDecl] uniqueSymbolType | semmle.label | [VarDecl] uniqueSymbolType | | tst.ts:31:7:31:44 | [VariableDeclarator] uniqueS ... = null | semmle.label | [VariableDeclarator] uniqueS ... = null | | tst.ts:31:25:31:37 | [KeywordTypeExpr] unique symbol | semmle.label | [KeywordTypeExpr] unique symbol | | tst.ts:31:41:31:44 | [Literal] null | semmle.label | [Literal] null | | tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.label | [DeclStmt] let objectType = ... | -| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 39 | +| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 40 | | tst.ts:32:5:32:14 | [VarDecl] objectType | semmle.label | [VarDecl] objectType | | tst.ts:32:5:32:22 | [VariableDeclarator] objectType: object | semmle.label | [VariableDeclarator] objectType: object | | tst.ts:32:17:32:22 | [KeywordTypeExpr] object | semmle.label | [KeywordTypeExpr] object | | tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.label | [DeclStmt] let intersection = ... | -| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 40 | +| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 41 | | tst.ts:33:5:33:16 | [VarDecl] intersection | semmle.label | [VarDecl] intersection | | tst.ts:33:5:33:38 | [VariableDeclarator] interse ... string} | semmle.label | [VariableDeclarator] interse ... string} | | tst.ts:33:19:33:24 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -371,14 +383,14 @@ nodes | tst.ts:33:29:33:37 | [FieldDeclaration] x: string | semmle.label | [FieldDeclaration] x: string | | tst.ts:33:32:33:37 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.label | [DeclStmt] let tuple = ... | -| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 41 | +| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 42 | | tst.ts:34:5:34:9 | [VarDecl] tuple | semmle.label | [VarDecl] tuple | | tst.ts:34:5:34:27 | [VariableDeclarator] tuple: ... string] | semmle.label | [VariableDeclarator] tuple: ... string] | | tst.ts:34:12:34:27 | [TupleTypeExpr] [number, string] | semmle.label | [TupleTypeExpr] [number, string] | | tst.ts:34:13:34:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:34:21:34:26 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.label | [DeclStmt] let tupleWithOptionalElement = ... | -| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 42 | +| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 43 | | tst.ts:36:5:36:28 | [VarDecl] tupleWithOptionalElement | semmle.label | [VarDecl] tupleWithOptionalElement | | tst.ts:36:5:36:55 | [VariableDeclarator] tupleWi ... umber?] | semmle.label | [VariableDeclarator] tupleWi ... umber?] | | tst.ts:36:31:36:55 | [TupleTypeExpr] [number ... umber?] | semmle.label | [TupleTypeExpr] [number ... umber?] | @@ -387,12 +399,12 @@ nodes | tst.ts:36:48:36:53 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:36:48:36:54 | [OptionalTypeExpr] number? | semmle.label | [OptionalTypeExpr] number? | | tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.label | [DeclStmt] let emptyTuple = ... | -| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 43 | +| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 44 | | tst.ts:37:5:37:14 | [VarDecl] emptyTuple | semmle.label | [VarDecl] emptyTuple | | tst.ts:37:5:37:18 | [VariableDeclarator] emptyTuple: [] | semmle.label | [VariableDeclarator] emptyTuple: [] | | tst.ts:37:17:37:18 | [TupleTypeExpr] [] | semmle.label | [TupleTypeExpr] [] | | tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.label | [DeclStmt] let tupleWithRestElement = ... | -| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 44 | +| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 45 | | tst.ts:38:5:38:24 | [VarDecl] tupleWithRestElement | semmle.label | [VarDecl] tupleWithRestElement | | tst.ts:38:5:38:47 | [VariableDeclarator] tupleWi ... ring[]] | semmle.label | [VariableDeclarator] tupleWi ... ring[]] | | tst.ts:38:27:38:47 | [TupleTypeExpr] [number ... ring[]] | semmle.label | [TupleTypeExpr] [number ... ring[]] | @@ -401,7 +413,7 @@ nodes | tst.ts:38:39:38:44 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:38:39:38:46 | [ArrayTypeExpr] string[] | semmle.label | [ArrayTypeExpr] string[] | | tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.label | [DeclStmt] let tupleWithOptionalAndRestElements = ... | -| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 45 | +| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 46 | | tst.ts:39:5:39:36 | [VarDecl] tupleWithOptionalAndRestElements | semmle.label | [VarDecl] tupleWithOptionalAndRestElements | | tst.ts:39:5:39:68 | [VariableDeclarator] tupleWi ... mber[]] | semmle.label | [VariableDeclarator] tupleWi ... mber[]] | | tst.ts:39:39:39:68 | [TupleTypeExpr] [number ... mber[]] | semmle.label | [TupleTypeExpr] [number ... mber[]] | @@ -412,12 +424,12 @@ nodes | tst.ts:39:60:39:65 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:39:60:39:67 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.label | [DeclStmt] let unknownType = ... | -| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 46 | +| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 47 | | tst.ts:40:5:40:15 | [VarDecl] unknownType | semmle.label | [VarDecl] unknownType | | tst.ts:40:5:40:24 | [VariableDeclarator] unknownType: unknown | semmle.label | [VariableDeclarator] unknownType: unknown | | tst.ts:40:18:40:24 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | | tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.label | [DeclStmt] let constArrayLiteral = ... | -| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 47 | +| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 48 | | tst.ts:42:5:42:21 | [VarDecl] constArrayLiteral | semmle.label | [VarDecl] constArrayLiteral | | tst.ts:42:5:42:39 | [VariableDeclarator] constAr ... s const | semmle.label | [VariableDeclarator] constAr ... s const | | tst.ts:42:25:42:30 | [ArrayExpr] [1, 2] | semmle.label | [ArrayExpr] [1, 2] | @@ -426,7 +438,7 @@ nodes | tst.ts:42:29:42:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:42:35:42:39 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.label | [DeclStmt] let constObjectLiteral = ... | -| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 48 | +| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 49 | | tst.ts:43:5:43:22 | [VarDecl] constObjectLiteral | semmle.label | [VarDecl] constObjectLiteral | | tst.ts:43:5:43:48 | [VariableDeclarator] constOb ... s const | semmle.label | [VariableDeclarator] constOb ... s const | | tst.ts:43:26:43:39 | [ObjectExpr] {foo: ...} | semmle.label | [ObjectExpr] {foo: ...} | @@ -436,7 +448,7 @@ nodes | tst.ts:43:33:43:37 | [Literal] "foo" | semmle.label | [Literal] "foo" | | tst.ts:43:44:43:48 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.label | [TryStmt] try { } ... ; } } | -| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 49 | +| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 50 | | tst.ts:46:5:46:7 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | | tst.ts:47:1:51:1 | [CatchClause] catch ( ... ; } } | semmle.label | [CatchClause] catch ( ... ; } } | | tst.ts:47:8:47:8 | [SimpleParameter] e | semmle.label | [SimpleParameter] e | @@ -453,21 +465,21 @@ nodes | tst.ts:49:15:49:20 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:49:24:49:24 | [VarRef] e | semmle.label | [VarRef] e | | tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 50 | +| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 51 | | tst.ts:54:11:54:26 | [Identifier] NonAbstractDummy | semmle.label | [Identifier] NonAbstractDummy | | tst.ts:55:3:55:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:55:3:55:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:55:3:55:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:55:14:55:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 51 | +| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 52 | | tst.ts:58:11:58:17 | [Identifier] HasArea | semmle.label | [Identifier] HasArea | | tst.ts:59:3:59:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:59:3:59:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:59:3:59:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:59:14:59:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.label | [DeclStmt] let Ctor = ... | -| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 52 | +| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 53 | | tst.ts:63:5:63:8 | [VarDecl] Ctor | semmle.label | [VarDecl] Ctor | | tst.ts:63:5:63:44 | [VariableDeclarator] Ctor: a ... = Shape | semmle.label | [VariableDeclarator] Ctor: a ... = Shape | | tst.ts:63:11:63:36 | [FunctionExpr] abstrac ... HasArea | semmle.label | [FunctionExpr] abstrac ... HasArea | @@ -475,7 +487,7 @@ nodes | tst.ts:63:30:63:36 | [LocalTypeAccess] HasArea | semmle.label | [LocalTypeAccess] HasArea | | tst.ts:63:40:63:44 | [VarRef] Shape | semmle.label | [VarRef] Shape | | tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 53 | +| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 54 | | tst.ts:65:6:65:12 | [Identifier] MyUnion | semmle.label | [Identifier] MyUnion | | tst.ts:65:16:65:30 | [InterfaceTypeExpr] {myUnion: true} | semmle.label | [InterfaceTypeExpr] {myUnion: true} | | tst.ts:65:16:65:53 | [UnionTypeExpr] {myUnio ... : true} | semmle.label | [UnionTypeExpr] {myUnio ... : true} | @@ -487,7 +499,7 @@ nodes | tst.ts:65:35:65:52 | [FieldDeclaration] stillMyUnion: true | semmle.label | [FieldDeclaration] stillMyUnion: true | | tst.ts:65:49:65:52 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.label | [DeclStmt] let union1 = ... | -| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 54 | +| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 55 | | tst.ts:66:5:66:10 | [VarDecl] union1 | semmle.label | [VarDecl] union1 | | tst.ts:66:5:66:37 | [VariableDeclarator] union1: ... : true} | semmle.label | [VariableDeclarator] union1: ... : true} | | tst.ts:66:13:66:19 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | @@ -496,7 +508,7 @@ nodes | tst.ts:66:24:66:36 | [Property] myUnion: true | semmle.label | [Property] myUnion: true | | tst.ts:66:33:66:36 | [Literal] true | semmle.label | [Literal] true | | tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 55 | +| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 56 | | tst.ts:68:6:68:13 | [Identifier] MyUnion2 | semmle.label | [Identifier] MyUnion2 | | tst.ts:68:17:68:23 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | | tst.ts:68:17:68:48 | [UnionTypeExpr] MyUnion ... : true} | semmle.label | [UnionTypeExpr] MyUnion ... : true} | @@ -505,7 +517,7 @@ nodes | tst.ts:68:28:68:47 | [FieldDeclaration] yetAnotherType: true | semmle.label | [FieldDeclaration] yetAnotherType: true | | tst.ts:68:44:68:47 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.label | [DeclStmt] let union2 = ... | -| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 56 | +| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 57 | | tst.ts:69:5:69:10 | [VarDecl] union2 | semmle.label | [VarDecl] union2 | | tst.ts:69:5:69:45 | [VariableDeclarator] union2: ... : true} | semmle.label | [VariableDeclarator] union2: ... : true} | | tst.ts:69:13:69:20 | [LocalTypeAccess] MyUnion2 | semmle.label | [LocalTypeAccess] MyUnion2 | @@ -514,7 +526,7 @@ nodes | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | semmle.label | [Property] yetAnotherType: true | | tst.ts:69:41:69:44 | [Literal] true | semmle.label | [Literal] true | | tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.label | [NamespaceDeclaration] module ... } } | -| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.order | 57 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.order | 58 | | tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.label | [VarDecl] TS43 | | tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | | tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.label | [Identifier] ThingI | @@ -679,7 +691,7 @@ nodes | tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.label | [DotExpr] this.#someValue | | tst.ts:127:19:127:28 | [Label] #someValue | semmle.label | [Label] #someValue | | tst.ts:132:1:193:1 | [NamespaceDeclaration] module ... } } | semmle.label | [NamespaceDeclaration] module ... } } | -| tst.ts:132:1:193:1 | [NamespaceDeclaration] module ... } } | semmle.order | 58 | +| tst.ts:132:1:193:1 | [NamespaceDeclaration] module ... } } | semmle.order | 59 | | tst.ts:132:8:132:11 | [VarDecl] TS44 | semmle.label | [VarDecl] TS44 | | tst.ts:133:3:138:3 | [FunctionDeclStmt] functio ... } } | semmle.label | [FunctionDeclStmt] functio ... } } | | tst.ts:133:12:133:14 | [VarDecl] foo | semmle.label | [VarDecl] foo | @@ -870,7 +882,7 @@ nodes | tst.ts:189:19:189:28 | [DotExpr] Foo.#count | semmle.label | [DotExpr] Foo.#count | | tst.ts:189:23:189:28 | [Label] #count | semmle.label | [Label] #count | | tst.ts:195:1:235:1 | [NamespaceDeclaration] module ... } } } | semmle.label | [NamespaceDeclaration] module ... } } } | -| tst.ts:195:1:235:1 | [NamespaceDeclaration] module ... } } } | semmle.order | 59 | +| tst.ts:195:1:235:1 | [NamespaceDeclaration] module ... } } } | semmle.order | 60 | | tst.ts:195:8:195:11 | [VarDecl] TS45 | semmle.label | [VarDecl] TS45 | | tst.ts:197:3:197:36 | [TypeAliasDeclaration,TypeDefinition] type A ... ring>>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type A ... ring>>; | | tst.ts:197:8:197:8 | [Identifier] A | semmle.label | [Identifier] A | @@ -983,21 +995,21 @@ nodes | tst.ts:232:28:232:38 | [DotExpr] other.#name | semmle.label | [DotExpr] other.#name | | tst.ts:232:34:232:38 | [Label] #name | semmle.label | [Label] #name | | tst.ts:237:1:237:63 | [ImportDeclaration] import ... son" }; | semmle.label | [ImportDeclaration] import ... son" }; | -| tst.ts:237:1:237:63 | [ImportDeclaration] import ... son" }; | semmle.order | 60 | +| tst.ts:237:1:237:63 | [ImportDeclaration] import ... son" }; | semmle.order | 61 | | tst.ts:237:8:237:16 | [ImportSpecifier] * as Foo3 | semmle.label | [ImportSpecifier] * as Foo3 | | tst.ts:237:13:237:16 | [VarDecl] Foo3 | semmle.label | [VarDecl] Foo3 | | tst.ts:237:23:237:40 | [Literal] "./something.json" | semmle.label | [Literal] "./something.json" | | tst.ts:237:47:237:62 | [ObjectExpr] { type: "json" } | semmle.label | [ObjectExpr] { type: "json" } | | tst.ts:237:49:237:60 | [Property] type: "json" | semmle.label | [Property] type: "json" | | tst.ts:238:1:238:19 | [DeclStmt] var foo = ... | semmle.label | [DeclStmt] var foo = ... | -| tst.ts:238:1:238:19 | [DeclStmt] var foo = ... | semmle.order | 61 | +| tst.ts:238:1:238:19 | [DeclStmt] var foo = ... | semmle.order | 62 | | tst.ts:238:5:238:7 | [VarDecl] foo | semmle.label | [VarDecl] foo | | tst.ts:238:5:238:18 | [VariableDeclarator] foo = Foo3.foo | semmle.label | [VariableDeclarator] foo = Foo3.foo | | tst.ts:238:11:238:14 | [VarRef] Foo3 | semmle.label | [VarRef] Foo3 | | tst.ts:238:11:238:18 | [DotExpr] Foo3.foo | semmle.label | [DotExpr] Foo3.foo | | tst.ts:238:16:238:18 | [Label] foo | semmle.label | [Label] foo | | tst.ts:240:1:296:1 | [NamespaceDeclaration] module ... }; } | semmle.label | [NamespaceDeclaration] module ... }; } | -| tst.ts:240:1:296:1 | [NamespaceDeclaration] module ... }; } | semmle.order | 62 | +| tst.ts:240:1:296:1 | [NamespaceDeclaration] module ... }; } | semmle.order | 63 | | tst.ts:240:8:240:11 | [VarDecl] TS46 | semmle.label | [VarDecl] TS46 | | tst.ts:241:3:241:15 | [ClassDefinition,TypeDefinition] class Base {} | semmle.label | [ClassDefinition,TypeDefinition] class Base {} | | tst.ts:241:9:241:12 | [VarDecl] Base | semmle.label | [VarDecl] Base | @@ -1192,13 +1204,13 @@ nodes | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | semmle.label | [ExprStmt] payload.toFixed(); | | tst.ts:293:15:293:21 | [Label] toFixed | semmle.label | [Label] toFixed | | tst.ts:298:1:298:21 | [DeclStmt] const key = ... | semmle.label | [DeclStmt] const key = ... | -| tst.ts:298:1:298:21 | [DeclStmt] const key = ... | semmle.order | 63 | +| tst.ts:298:1:298:21 | [DeclStmt] const key = ... | semmle.order | 64 | | tst.ts:298:7:298:9 | [VarDecl] key | semmle.label | [VarDecl] key | | tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | semmle.label | [VariableDeclarator] key = Symbol() | | tst.ts:298:13:298:18 | [VarRef] Symbol | semmle.label | [VarRef] Symbol | | tst.ts:298:13:298:20 | [CallExpr] Symbol() | semmle.label | [CallExpr] Symbol() | | tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | semmle.label | [DeclStmt] const numberOrString = ... | -| tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | semmle.order | 64 | +| tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | semmle.order | 65 | | tst.ts:300:7:300:20 | [VarDecl] numberOrString | semmle.label | [VarDecl] numberOrString | | tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | semmle.label | [VariableDeclarator] numberO ... "hello" | | tst.ts:300:24:300:27 | [VarRef] Math | semmle.label | [VarRef] Math | @@ -1211,7 +1223,7 @@ nodes | tst.ts:300:46:300:47 | [Literal] 42 | semmle.label | [Literal] 42 | | tst.ts:300:51:300:57 | [Literal] "hello" | semmle.label | [Literal] "hello" | | tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | semmle.label | [DeclStmt] let obj = ... | -| tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | semmle.order | 65 | +| tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | semmle.order | 66 | | tst.ts:302:5:302:7 | [VarDecl] obj | semmle.label | [VarDecl] obj | | tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | semmle.label | [VariableDeclarator] obj = { ... ring, } | | tst.ts:302:11:304:1 | [ObjectExpr] { [ke ... ring, } | semmle.label | [ObjectExpr] { [ke ... ring, } | @@ -1219,7 +1231,7 @@ nodes | tst.ts:303:4:303:6 | [VarRef] key | semmle.label | [VarRef] key | | tst.ts:303:10:303:23 | [VarRef] numberOrString | semmle.label | [VarRef] numberOrString | | tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | semmle.label | [IfStmt] if (typ ... se(); } | -| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | semmle.order | 66 | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | semmle.order | 67 | | tst.ts:306:5:306:19 | [UnaryExpr] typeof obj[key] | semmle.label | [UnaryExpr] typeof obj[key] | | tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | semmle.label | [BinaryExpr] typeof ... string" | | tst.ts:306:12:306:14 | [VarRef] obj | semmle.label | [VarRef] obj | @@ -1239,7 +1251,7 @@ nodes | tst.ts:308:3:308:20 | [ExprStmt] str.toUpperCase(); | semmle.label | [ExprStmt] str.toUpperCase(); | | tst.ts:308:7:308:17 | [Label] toUpperCase | semmle.label | [Label] toUpperCase | | tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | semmle.label | [FunctionDeclStmt] functio ... void {} | -| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | semmle.order | 67 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | semmle.order | 68 | | tst.ts:313:10:313:10 | [VarDecl] f | semmle.label | [VarDecl] f | | tst.ts:313:12:313:12 | [Identifier] T | semmle.label | [Identifier] T | | tst.ts:313:12:313:12 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -1262,11 +1274,11 @@ nodes | tst.ts:316:4:316:7 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | | tst.ts:316:9:316:10 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | tst.ts:316:11:316:11 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | -| tst.ts:316:11:316:11 | [EmptyStmt] ; | semmle.order | 68 | +| tst.ts:316:11:316:11 | [EmptyStmt] ; | semmle.order | 69 | | tst.ts:318:1:318:1 | [VarRef] f | semmle.label | [VarRef] f | | tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | semmle.label | [CallExpr] f({ p ... se() }) | | tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | semmle.label | [ExprStmt] f({ p ... e() }); | -| tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | semmle.order | 69 | +| tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | semmle.order | 70 | | tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | semmle.label | [ObjectExpr] {produce: ...} | | tst.ts:319:3:319:9 | [Label] produce | semmle.label | [Label] produce | | tst.ts:319:3:319:17 | [Property] produce: n => n | semmle.label | [Property] produce: n => n | @@ -1282,7 +1294,7 @@ nodes | tst.ts:320:17:320:31 | [MethodCallExpr] x.toLowerCase() | semmle.label | [MethodCallExpr] x.toLowerCase() | | tst.ts:320:19:320:29 | [Label] toLowerCase | semmle.label | [Label] toLowerCase | | tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | semmle.label | [DeclStmt] const ErrorMap = ... | -| tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | semmle.order | 70 | +| tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | semmle.order | 71 | | tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.label | [VarDecl] ErrorMap | | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | [VariableDeclarator] ErrorMa ... Error> | | tst.ts:325:18:325:20 | [VarRef] Map | semmle.label | [VarRef] Map | @@ -1290,13 +1302,13 @@ nodes | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.label | [LocalTypeAccess] Error | | tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | semmle.label | [DeclStmt] const errorMap = ... | -| tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | semmle.order | 71 | +| tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | semmle.order | 72 | | tst.ts:327:7:327:14 | [VarDecl] errorMap | semmle.label | [VarDecl] errorMap | | tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | semmle.label | [VariableDeclarator] errorMa ... orMap() | | tst.ts:327:18:327:31 | [NewExpr] new ErrorMap() | semmle.label | [NewExpr] new ErrorMap() | | tst.ts:327:22:327:29 | [VarRef] ErrorMap | semmle.label | [VarRef] ErrorMap | | tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | -| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.order | 72 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.order | 73 | | tst.ts:331:6:331:16 | [Identifier] FirstString | semmle.label | [Identifier] FirstString | | tst.ts:331:18:331:18 | [Identifier] T | semmle.label | [Identifier] T | | tst.ts:331:18:331:18 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -1313,7 +1325,7 @@ nodes | tst.ts:333:9:333:9 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | | tst.ts:334:9:334:13 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | | tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | -| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.order | 73 | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.order | 74 | | tst.ts:336:6:336:6 | [Identifier] F | semmle.label | [Identifier] F | | tst.ts:336:10:336:20 | [LocalTypeAccess] FirstString | semmle.label | [LocalTypeAccess] FirstString | | tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | [GenericTypeExpr] FirstSt ... olean]> | @@ -1324,13 +1336,13 @@ nodes | tst.ts:336:34:336:39 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:336:42:336:48 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | | tst.ts:338:1:338:17 | [DeclStmt] const a = ... | semmle.label | [DeclStmt] const a = ... | -| tst.ts:338:1:338:17 | [DeclStmt] const a = ... | semmle.order | 74 | +| tst.ts:338:1:338:17 | [DeclStmt] const a = ... | semmle.order | 75 | | tst.ts:338:7:338:7 | [VarDecl] a | semmle.label | [VarDecl] a | | tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | semmle.label | [VariableDeclarator] a: F = 'a' | | tst.ts:338:10:338:10 | [LocalTypeAccess] F | semmle.label | [LocalTypeAccess] F | | tst.ts:338:14:338:16 | [Literal] 'a' | semmle.label | [Literal] 'a' | | tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | -| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | semmle.order | 75 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | semmle.order | 76 | | tst.ts:342:11:342:15 | [Identifier] State | semmle.label | [Identifier] State | | tst.ts:342:17:342:24 | [TypeParameter] in out T | semmle.label | [TypeParameter] in out T | | tst.ts:342:24:342:24 | [Identifier] T | semmle.label | [Identifier] T | @@ -1347,7 +1359,7 @@ nodes | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | tst.ts:344:22:344:25 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | | tst.ts:347:1:350:1 | [DeclStmt] const state = ... | semmle.label | [DeclStmt] const state = ... | -| tst.ts:347:1:350:1 | [DeclStmt] const state = ... | semmle.order | 76 | +| tst.ts:347:1:350:1 | [DeclStmt] const state = ... | semmle.order | 77 | | tst.ts:347:7:347:11 | [VarDecl] state | semmle.label | [VarDecl] state | | tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | semmle.label | [VariableDeclarator] state: ... > { } } | | tst.ts:347:14:347:18 | [LocalTypeAccess] State | semmle.label | [LocalTypeAccess] State | @@ -1364,7 +1376,7 @@ nodes | tst.ts:349:9:349:13 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | | tst.ts:349:19:349:21 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | | tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | semmle.label | [DeclStmt] const fortyTwo = ... | -| tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | semmle.order | 77 | +| tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | semmle.order | 78 | | tst.ts:352:7:352:14 | [VarDecl] fortyTwo | semmle.label | [VarDecl] fortyTwo | | tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | [VariableDeclarator] fortyTw ... e.get() | | tst.ts:352:18:352:22 | [VarRef] state | semmle.label | [VarRef] state | @@ -1372,7 +1384,7 @@ nodes | tst.ts:352:18:352:28 | [MethodCallExpr] state.get() | semmle.label | [MethodCallExpr] state.get() | | tst.ts:352:24:352:26 | [Label] get | semmle.label | [Label] get | | tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | semmle.label | [ImportDeclaration] import ... S.mjs'; | -| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | semmle.order | 78 | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | semmle.order | 79 | | tst.ts:356:8:356:18 | [ImportSpecifier] tstModuleES | semmle.label | [ImportSpecifier] tstModuleES | | tst.ts:356:8:356:18 | [VarDecl] tstModuleES | semmle.label | [VarDecl] tstModuleES | | tst.ts:356:25:356:43 | [Literal] './tstModuleES.mjs' | semmle.label | [Literal] './tstModuleES.mjs' | @@ -1380,12 +1392,12 @@ nodes | tst.ts:358:1:358:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | | tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | semmle.label | [MethodCallExpr] console ... leES()) | | tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | semmle.label | [ExprStmt] console ... eES()); | -| tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | semmle.order | 79 | +| tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | semmle.order | 80 | | tst.ts:358:9:358:11 | [Label] log | semmle.label | [Label] log | | tst.ts:358:13:358:23 | [VarRef] tstModuleES | semmle.label | [VarRef] tstModuleES | | tst.ts:358:13:358:25 | [CallExpr] tstModuleES() | semmle.label | [CallExpr] tstModuleES() | | tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | semmle.label | [ImportDeclaration] import ... S.cjs'; | -| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | semmle.order | 80 | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | semmle.order | 81 | | tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | semmle.label | [ImportSpecifier] tstModuleCJS | | tst.ts:360:10:360:21 | [Label] tstModuleCJS | semmle.label | [Label] tstModuleCJS | | tst.ts:360:10:360:21 | [VarDecl] tstModuleCJS | semmle.label | [VarDecl] tstModuleCJS | @@ -1394,12 +1406,12 @@ nodes | tst.ts:362:1:362:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | | tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | semmle.label | [MethodCallExpr] console ... eCJS()) | | tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | semmle.label | [ExprStmt] console ... CJS()); | -| tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | semmle.order | 81 | +| tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | semmle.order | 82 | | tst.ts:362:9:362:11 | [Label] log | semmle.label | [Label] log | | tst.ts:362:13:362:24 | [VarRef] tstModuleCJS | semmle.label | [VarRef] tstModuleCJS | | tst.ts:362:13:362:26 | [CallExpr] tstModuleCJS() | semmle.label | [CallExpr] tstModuleCJS() | | tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | semmle.label | [ImportDeclaration] import ... ffixA'; | -| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | semmle.order | 82 | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | semmle.order | 83 | | tst.ts:368:8:368:13 | [ImportSpecifier] * as A | semmle.label | [ImportSpecifier] * as A | | tst.ts:368:13:368:13 | [VarDecl] A | semmle.label | [VarDecl] A | | tst.ts:368:20:368:33 | [Literal] './tstSuffixA' | semmle.label | [Literal] './tstSuffixA' | @@ -1407,14 +1419,14 @@ nodes | tst.ts:370:1:370:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | | tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | semmle.label | [MethodCallExpr] console ... File()) | | tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | semmle.label | [ExprStmt] console ... ile()); | -| tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | semmle.order | 83 | +| tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | semmle.order | 84 | | tst.ts:370:9:370:11 | [Label] log | semmle.label | [Label] log | | tst.ts:370:13:370:13 | [VarRef] A | semmle.label | [VarRef] A | | tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | semmle.label | [DotExpr] A.resolvedFile | | tst.ts:370:13:370:28 | [MethodCallExpr] A.resolvedFile() | semmle.label | [MethodCallExpr] A.resolvedFile() | | tst.ts:370:15:370:26 | [Label] resolvedFile | semmle.label | [Label] resolvedFile | | tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | semmle.label | [ImportDeclaration] import ... ffixB'; | -| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | semmle.order | 84 | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | semmle.order | 85 | | tst.ts:372:8:372:13 | [ImportSpecifier] * as B | semmle.label | [ImportSpecifier] * as B | | tst.ts:372:13:372:13 | [VarDecl] B | semmle.label | [VarDecl] B | | tst.ts:372:20:372:33 | [Literal] './tstSuffixB' | semmle.label | [Literal] './tstSuffixB' | @@ -1422,14 +1434,14 @@ nodes | tst.ts:374:1:374:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | | tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | semmle.label | [MethodCallExpr] console ... File()) | | tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | semmle.label | [ExprStmt] console ... ile()); | -| tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | semmle.order | 85 | +| tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | semmle.order | 86 | | tst.ts:374:9:374:11 | [Label] log | semmle.label | [Label] log | | tst.ts:374:13:374:13 | [VarRef] B | semmle.label | [VarRef] B | | tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | semmle.label | [DotExpr] B.resolvedFile | | tst.ts:374:13:374:28 | [MethodCallExpr] B.resolvedFile() | semmle.label | [MethodCallExpr] B.resolvedFile() | | tst.ts:374:15:374:26 | [Label] resolvedFile | semmle.label | [Label] resolvedFile | | tst.ts:379:1:386:1 | [NamespaceDeclaration] module ... ; } | semmle.label | [NamespaceDeclaration] module ... ; } | -| tst.ts:379:1:386:1 | [NamespaceDeclaration] module ... ; } | semmle.order | 86 | +| tst.ts:379:1:386:1 | [NamespaceDeclaration] module ... ; } | semmle.order | 87 | | tst.ts:379:8:379:11 | [VarDecl] TS48 | semmle.label | [VarDecl] TS48 | | tst.ts:381:5:381:73 | [TypeAliasDeclaration,TypeDefinition] type So ... never; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type So ... never; | | tst.ts:381:10:381:16 | [Identifier] SomeNum | semmle.label | [Identifier] SomeNum | @@ -1468,7 +1480,7 @@ nodes | tst.ts:385:59:385:63 | [Literal] false | semmle.label | [Literal] false | | tst.ts:385:66:385:71 | [Literal] "bye!" | semmle.label | [Literal] "bye!" | | tst.ts:390:1:426:1 | [NamespaceDeclaration] module ... } } } | semmle.label | [NamespaceDeclaration] module ... } } } | -| tst.ts:390:1:426:1 | [NamespaceDeclaration] module ... } } } | semmle.order | 87 | +| tst.ts:390:1:426:1 | [NamespaceDeclaration] module ... } } } | semmle.order | 88 | | tst.ts:390:8:390:11 | [VarDecl] TS49 | semmle.label | [VarDecl] TS49 | | tst.ts:391:3:391:41 | [TypeAliasDeclaration,TypeDefinition] type Co ... "blue"; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Co ... "blue"; | | tst.ts:391:8:391:13 | [Identifier] Colors | semmle.label | [Identifier] Colors | @@ -1565,7 +1577,7 @@ nodes | tst.ts:423:12:423:15 | [Label] name | semmle.label | [Label] name | | tst.ts:423:19:423:22 | [VarRef] name | semmle.label | [VarRef] name | | tst.ts:430:1:468:1 | [NamespaceDeclaration] module ... - "b" } | semmle.label | [NamespaceDeclaration] module ... - "b" } | -| tst.ts:430:1:468:1 | [NamespaceDeclaration] module ... - "b" } | semmle.order | 88 | +| tst.ts:430:1:468:1 | [NamespaceDeclaration] module ... - "b" } | semmle.order | 89 | | tst.ts:430:8:430:11 | [VarDecl] TS50 | semmle.label | [VarDecl] TS50 | | tst.ts:431:5:445:5 | [FunctionDeclStmt] functio ... ; } | semmle.label | [FunctionDeclStmt] functio ... ; } | | tst.ts:431:14:431:25 | [VarDecl] loggedMethod | semmle.label | [VarDecl] loggedMethod | @@ -1715,7 +1727,7 @@ nodes | tst.ts:467:15:467:20 | [IndexExpr] foo[1] | semmle.label | [IndexExpr] foo[1] | | tst.ts:467:19:467:19 | [Literal] 1 | semmle.label | [Literal] 1 | | tst.ts:472:1:484:1 | [NamespaceDeclaration] module ... ng>); } | semmle.label | [NamespaceDeclaration] module ... ng>); } | -| tst.ts:472:1:484:1 | [NamespaceDeclaration] module ... ng>); } | semmle.order | 89 | +| tst.ts:472:1:484:1 | [NamespaceDeclaration] module ... ng>); } | semmle.order | 90 | | tst.ts:472:8:472:11 | [VarDecl] TS52 | semmle.label | [VarDecl] TS52 | | tst.ts:473:5:476:5 | [ClassDefinition,TypeDefinition] class S ... ; } | semmle.label | [ClassDefinition,TypeDefinition] class S ... ; } | | tst.ts:473:11:473:19 | [VarDecl] SomeClass | semmle.label | [VarDecl] SomeClass | @@ -1763,7 +1775,7 @@ nodes | tst.ts:483:46:483:58 | [GenericTypeExpr] Pair3 | semmle.label | [GenericTypeExpr] Pair3 | | tst.ts:483:52:483:57 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:486:1:496:1 | [NamespaceDeclaration] module ... }); } | semmle.label | [NamespaceDeclaration] module ... }); } | -| tst.ts:486:1:496:1 | [NamespaceDeclaration] module ... }); } | semmle.order | 90 | +| tst.ts:486:1:496:1 | [NamespaceDeclaration] module ... }); } | semmle.order | 91 | | tst.ts:486:8:486:11 | [VarDecl] TS54 | semmle.label | [VarDecl] TS54 | | tst.ts:487:3:489:3 | [FunctionDeclStmt] functio ... 0]; } | semmle.label | [FunctionDeclStmt] functio ... 0]; } | | tst.ts:487:12:487:28 | [VarDecl] createStreetLight | semmle.label | [VarDecl] createStreetLight | @@ -1818,7 +1830,7 @@ nodes | tst.ts:494:28:494:33 | [Literal] "even" | semmle.label | [Literal] "even" | | tst.ts:494:36:494:40 | [Literal] "odd" | semmle.label | [Literal] "odd" | | tst.ts:498:1:511:1 | [NamespaceDeclaration] module ... } } } | semmle.label | [NamespaceDeclaration] module ... } } } | -| tst.ts:498:1:511:1 | [NamespaceDeclaration] module ... } } } | semmle.order | 91 | +| tst.ts:498:1:511:1 | [NamespaceDeclaration] module ... } } } | semmle.order | 92 | | tst.ts:498:8:498:11 | [VarDecl] TS55 | semmle.label | [VarDecl] TS55 | | tst.ts:499:3:500:40 | [DeclStmt] const strings = ... | semmle.label | [DeclStmt] const strings = ... | | tst.ts:499:9:499:15 | [VarDecl] strings | semmle.label | [VarDecl] strings | @@ -1875,7 +1887,7 @@ nodes | tst.ts:508:21:508:23 | [VarRef] key | semmle.label | [VarRef] key | | tst.ts:508:26:508:36 | [Label] toUpperCase | semmle.label | [Label] toUpperCase | | tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | semmle.label | [NamespaceDeclaration] namespa ... type. } | -| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | semmle.order | 92 | +| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | semmle.order | 93 | | tst.ts:513:11:513:14 | [VarDecl] TS57 | semmle.label | [VarDecl] TS57 | | tst.ts:514:3:514:26 | [DeclStmt] const a = ... | semmle.label | [DeclStmt] const a = ... | | tst.ts:514:17:514:17 | [VarDecl] a | semmle.label | [VarDecl] a | @@ -1902,7 +1914,7 @@ nodes | tst.ts:519:23:519:30 | [TypeofTypeExpr] typeof a | semmle.label | [TypeofTypeExpr] typeof a | | tst.ts:519:30:519:30 | [LocalVarTypeAccess] a | semmle.label | [LocalVarTypeAccess] a | | tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.label | [ExportDeclaration] export ... 'b'; } | -| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 93 | +| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 94 | | tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.label | [FunctionDeclStmt] functio ... 'b'; } | | tstModuleCJS.cts:1:17:1:28 | [VarDecl] tstModuleCJS | semmle.label | [VarDecl] tstModuleCJS | | tstModuleCJS.cts:1:33:1:35 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' | @@ -1920,7 +1932,7 @@ nodes | tstModuleCJS.cts:2:34:2:36 | [Literal] 'a' | semmle.label | [Literal] 'a' | | tstModuleCJS.cts:2:40:2:42 | [Literal] 'b' | semmle.label | [Literal] 'b' | | tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.label | [ExportDeclaration] export ... 'b'; } | -| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 94 | +| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 95 | | tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.label | [FunctionDeclStmt] functio ... 'b'; } | | tstModuleES.mts:1:25:1:35 | [VarDecl] tstModuleES | semmle.label | [VarDecl] tstModuleES | | tstModuleES.mts:1:40:1:42 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' | @@ -1938,7 +1950,7 @@ nodes | tstModuleES.mts:2:34:2:36 | [Literal] 'a' | semmle.label | [Literal] 'a' | | tstModuleES.mts:2:40:2:42 | [Literal] 'b' | semmle.label | [Literal] 'b' | | tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.label | [ExportDeclaration] export ... .ts'; } | -| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 95 | +| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 96 | | tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | [FunctionDeclStmt] functio ... .ts'; } | | tstSuffixA.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | [VarDecl] resolvedFile | | tstSuffixA.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixA.ts' | semmle.label | [LiteralTypeExpr] 'tstSuffixA.ts' | @@ -1946,7 +1958,7 @@ nodes | tstSuffixA.ts:2:5:2:27 | [ReturnStmt] return ... xA.ts'; | semmle.label | [ReturnStmt] return ... xA.ts'; | | tstSuffixA.ts:2:12:2:26 | [Literal] 'tstSuffixA.ts' | semmle.label | [Literal] 'tstSuffixA.ts' | | tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.label | [ExportDeclaration] export ... .ts'; } | -| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 96 | +| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 97 | | tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | [FunctionDeclStmt] functio ... .ts'; } | | tstSuffixB.ios.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | [VarDecl] resolvedFile | | tstSuffixB.ios.ts:1:33:1:51 | [LiteralTypeExpr] 'tstSuffixB.ios.ts' | semmle.label | [LiteralTypeExpr] 'tstSuffixB.ios.ts' | @@ -1954,7 +1966,7 @@ nodes | tstSuffixB.ios.ts:2:5:2:31 | [ReturnStmt] return ... os.ts'; | semmle.label | [ReturnStmt] return ... os.ts'; | | tstSuffixB.ios.ts:2:12:2:30 | [Literal] 'tstSuffixB.ios.ts' | semmle.label | [Literal] 'tstSuffixB.ios.ts' | | tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.label | [ExportDeclaration] export ... .ts'; } | -| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 97 | +| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 98 | | tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | [FunctionDeclStmt] functio ... .ts'; } | | tstSuffixB.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | [VarDecl] resolvedFile | | tstSuffixB.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixB.ts' | semmle.label | [LiteralTypeExpr] 'tstSuffixB.ts' | @@ -1962,16 +1974,16 @@ nodes | tstSuffixB.ts:2:5:2:27 | [ReturnStmt] return ... xB.ts'; | semmle.label | [ReturnStmt] return ... xB.ts'; | | tstSuffixB.ts:2:12:2:26 | [Literal] 'tstSuffixB.ts' | semmle.label | [Literal] 'tstSuffixB.ts' | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | -| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 98 | +| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 99 | | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | [Identifier] B | | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | | type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.label | [DeclStmt] var b = ... | -| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 99 | +| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 100 | | type_alias.ts:3:5:3:5 | [VarDecl] b | semmle.label | [VarDecl] b | | type_alias.ts:3:5:3:8 | [VariableDeclarator] b: B | semmle.label | [VariableDeclarator] b: B | | type_alias.ts:3:8:3:8 | [LocalTypeAccess] B | semmle.label | [LocalTypeAccess] B | | type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | -| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 100 | +| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 101 | | type_alias.ts:5:6:5:17 | [Identifier] ValueOrArray | semmle.label | [Identifier] ValueOrArray | | type_alias.ts:5:19:5:19 | [Identifier] T | semmle.label | [Identifier] T | | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -1983,14 +1995,14 @@ nodes | type_alias.ts:5:34:5:48 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:5:47:5:47 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.label | [DeclStmt] var c = ... | -| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 101 | +| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 102 | | type_alias.ts:7:5:7:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_alias.ts:7:5:7:27 | [VariableDeclarator] c: Valu ... number> | semmle.label | [VariableDeclarator] c: Valu ... number> | | type_alias.ts:7:8:7:19 | [LocalTypeAccess] ValueOrArray | semmle.label | [LocalTypeAccess] ValueOrArray | | type_alias.ts:7:8:7:27 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:7:21:7:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | -| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 102 | +| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 103 | | type_alias.ts:9:6:9:9 | [Identifier] Json | semmle.label | [Identifier] Json | | type_alias.ts:10:5:15:12 | [UnionTypeExpr] \| strin ... Json[] | semmle.label | [UnionTypeExpr] \| strin ... Json[] | | type_alias.ts:10:7:10:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -2006,12 +2018,12 @@ nodes | type_alias.ts:15:7:15:10 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:15:7:15:12 | [ArrayTypeExpr] Json[] | semmle.label | [ArrayTypeExpr] Json[] | | type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.label | [DeclStmt] var json = ... | -| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 103 | +| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 104 | | type_alias.ts:17:5:17:8 | [VarDecl] json | semmle.label | [VarDecl] json | | type_alias.ts:17:5:17:14 | [VariableDeclarator] json: Json | semmle.label | [VariableDeclarator] json: Json | | type_alias.ts:17:11:17:14 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | -| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 104 | +| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 105 | | type_alias.ts:19:6:19:16 | [Identifier] VirtualNode | semmle.label | [Identifier] VirtualNode | | type_alias.ts:20:5:21:56 | [UnionTypeExpr] \| strin ... Node[]] | semmle.label | [UnionTypeExpr] \| strin ... Node[]] | | type_alias.ts:20:7:20:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -2027,7 +2039,7 @@ nodes | type_alias.ts:21:43:21:53 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | | type_alias.ts:21:43:21:55 | [ArrayTypeExpr] VirtualNode[] | semmle.label | [ArrayTypeExpr] VirtualNode[] | | type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.label | [DeclStmt] const myNode = ... | -| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 105 | +| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 106 | | type_alias.ts:23:7:23:12 | [VarDecl] myNode | semmle.label | [VarDecl] myNode | | type_alias.ts:23:7:27:5 | [VariableDeclarator] myNode: ... ] ] | semmle.label | [VariableDeclarator] myNode: ... ] ] | | type_alias.ts:23:15:23:25 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | @@ -2052,12 +2064,12 @@ nodes | type_alias.ts:26:23:26:36 | [Literal] "second-child" | semmle.label | [Literal] "second-child" | | type_alias.ts:26:41:26:62 | [Literal] "I'm the second child" | semmle.label | [Literal] "I'm the second child" | | type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 106 | +| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 107 | | type_definition_objects.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definition_objects.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definition_objects.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.label | [ExportDeclaration] export class C {} | -| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 107 | +| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 108 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} | | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | @@ -2065,36 +2077,36 @@ nodes | type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | | type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... | -| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 108 | +| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 109 | | type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj | | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | [VariableDeclarator] classObj = C | | type_definition_objects.ts:4:16:4:16 | [VarRef] C | semmle.label | [VarRef] C | | type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.label | [ExportDeclaration] export enum E {} | -| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 109 | +| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 110 | | type_definition_objects.ts:6:8:6:16 | [EnumDeclaration,TypeDefinition] enum E {} | semmle.label | [EnumDeclaration,TypeDefinition] enum E {} | | type_definition_objects.ts:6:13:6:13 | [VarDecl] E | semmle.label | [VarDecl] E | | type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.label | [DeclStmt] let enumObj = ... | -| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 110 | +| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 111 | | type_definition_objects.ts:7:5:7:11 | [VarDecl] enumObj | semmle.label | [VarDecl] enumObj | | type_definition_objects.ts:7:5:7:15 | [VariableDeclarator] enumObj = E | semmle.label | [VariableDeclarator] enumObj = E | | type_definition_objects.ts:7:15:7:15 | [VarRef] E | semmle.label | [VarRef] E | | type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.label | [ExportDeclaration] export ... e N {;} | -| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 111 | +| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 112 | | type_definition_objects.ts:9:8:9:22 | [NamespaceDeclaration] namespace N {;} | semmle.label | [NamespaceDeclaration] namespace N {;} | | type_definition_objects.ts:9:18:9:18 | [VarDecl] N | semmle.label | [VarDecl] N | | type_definition_objects.ts:9:21:9:21 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | | type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.label | [DeclStmt] let namespaceObj = ... | -| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 112 | +| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 113 | | type_definition_objects.ts:10:5:10:16 | [VarDecl] namespaceObj | semmle.label | [VarDecl] namespaceObj | | type_definition_objects.ts:10:5:10:20 | [VariableDeclarator] namespaceObj = N | semmle.label | [VariableDeclarator] namespaceObj = N | | type_definition_objects.ts:10:20:10:20 | [VarRef] N | semmle.label | [VarRef] N | | type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 113 | +| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 114 | | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | -| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 114 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 115 | | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I | | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S | @@ -2102,14 +2114,14 @@ nodes | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | [FieldDeclaration] x: S; | | type_definitions.ts:4:6:4:6 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | | type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.label | [DeclStmt] let i = ... | -| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 115 | +| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 116 | | type_definitions.ts:6:5:6:5 | [VarDecl] i | semmle.label | [VarDecl] i | | type_definitions.ts:6:5:6:16 | [VariableDeclarator] i: I | semmle.label | [VariableDeclarator] i: I | | type_definitions.ts:6:8:6:8 | [LocalTypeAccess] I | semmle.label | [LocalTypeAccess] I | | type_definitions.ts:6:8:6:16 | [GenericTypeExpr] I | semmle.label | [GenericTypeExpr] I | | type_definitions.ts:6:10:6:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } | -| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 116 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 117 | | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | @@ -2121,14 +2133,14 @@ nodes | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | [FieldDeclaration] x: T | | type_definitions.ts:9:6:9:6 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.label | [DeclStmt] let c = ... | -| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 117 | +| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 118 | | type_definitions.ts:11:5:11:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_definitions.ts:11:5:11:16 | [VariableDeclarator] c: C | semmle.label | [VariableDeclarator] c: C | | type_definitions.ts:11:8:11:8 | [LocalTypeAccess] C | semmle.label | [LocalTypeAccess] C | | type_definitions.ts:11:8:11:16 | [GenericTypeExpr] C | semmle.label | [GenericTypeExpr] C | | type_definitions.ts:11:10:11:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.label | [EnumDeclaration,TypeDefinition] enum Co ... blue } | -| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 118 | +| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 119 | | type_definitions.ts:13:6:13:10 | [VarDecl] Color | semmle.label | [VarDecl] Color | | type_definitions.ts:14:3:14:5 | [EnumMember,TypeDefinition] red | semmle.label | [EnumMember,TypeDefinition] red | | type_definitions.ts:14:3:14:5 | [VarDecl] red | semmle.label | [VarDecl] red | @@ -2137,29 +2149,29 @@ nodes | type_definitions.ts:14:15:14:18 | [EnumMember,TypeDefinition] blue | semmle.label | [EnumMember,TypeDefinition] blue | | type_definitions.ts:14:15:14:18 | [VarDecl] blue | semmle.label | [VarDecl] blue | | type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.label | [DeclStmt] let color = ... | -| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 119 | +| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 120 | | type_definitions.ts:16:5:16:9 | [VarDecl] color | semmle.label | [VarDecl] color | | type_definitions.ts:16:5:16:16 | [VariableDeclarator] color: Color | semmle.label | [VariableDeclarator] color: Color | | type_definitions.ts:16:12:16:16 | [LocalTypeAccess] Color | semmle.label | [LocalTypeAccess] Color | | type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.label | [EnumDeclaration,TypeDefinition] enum En ... ember } | -| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 120 | +| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 121 | | type_definitions.ts:18:6:18:22 | [VarDecl] EnumWithOneMember | semmle.label | [VarDecl] EnumWithOneMember | | type_definitions.ts:18:26:18:31 | [EnumMember,TypeDefinition] member | semmle.label | [EnumMember,TypeDefinition] member | | type_definitions.ts:18:26:18:31 | [VarDecl] member | semmle.label | [VarDecl] member | | type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.label | [DeclStmt] let e = ... | -| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 121 | +| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 122 | | type_definitions.ts:19:5:19:5 | [VarDecl] e | semmle.label | [VarDecl] e | | type_definitions.ts:19:5:19:24 | [VariableDeclarator] e: EnumWithOneMember | semmle.label | [VariableDeclarator] e: EnumWithOneMember | | type_definitions.ts:19:8:19:24 | [LocalTypeAccess] EnumWithOneMember | semmle.label | [LocalTypeAccess] EnumWithOneMember | | type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | -| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 122 | +| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 123 | | type_definitions.ts:21:6:21:10 | [Identifier] Alias | semmle.label | [Identifier] Alias | | type_definitions.ts:21:12:21:12 | [Identifier] T | semmle.label | [Identifier] T | | type_definitions.ts:21:12:21:12 | [TypeParameter] T | semmle.label | [TypeParameter] T | | type_definitions.ts:21:17:21:17 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:21:17:21:19 | [ArrayTypeExpr] T[] | semmle.label | [ArrayTypeExpr] T[] | | type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.label | [DeclStmt] let aliasForNumberArray = ... | -| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 123 | +| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 124 | | type_definitions.ts:22:5:22:23 | [VarDecl] aliasForNumberArray | semmle.label | [VarDecl] aliasForNumberArray | | type_definitions.ts:22:5:22:38 | [VariableDeclarator] aliasFo ... number> | semmle.label | [VariableDeclarator] aliasFo ... number> | | type_definitions.ts:22:26:22:30 | [LocalTypeAccess] Alias | semmle.label | [LocalTypeAccess] Alias | @@ -2546,6 +2558,26 @@ edges | middle-rest.ts:3:7:3:26 | [ArrayExpr] [true, "hello", 123] | middle-rest.ts:3:14:3:20 | [Literal] "hello" | semmle.order | 2 | | middle-rest.ts:3:7:3:26 | [ArrayExpr] [true, "hello", 123] | middle-rest.ts:3:23:3:25 | [Literal] 123 | semmle.label | 3 | | middle-rest.ts:3:7:3:26 | [ArrayExpr] [true, "hello", 123] | middle-rest.ts:3:23:3:25 | [Literal] 123 | semmle.order | 3 | +| tsconfig.json:1:1:9:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | semmle.label | 0 | +| tsconfig.json:1:1:9:1 | [JsonObject] {compilerOptions: ...} | tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | semmle.order | 0 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:3:15:3:22 | [JsonString] "esnext" | semmle.label | 0 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:3:15:3:22 | [JsonString] "esnext" | semmle.order | 0 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:4:15:4:22 | [JsonString] "esnext" | semmle.label | 1 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:4:15:4:22 | [JsonString] "esnext" | semmle.order | 1 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | semmle.label | 2 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | semmle.order | 2 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:6:26:6:29 | [JsonBoolean] true | semmle.label | 3 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:6:26:6:29 | [JsonBoolean] true | semmle.order | 3 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | semmle.label | 4 | +| tsconfig.json:2:22:8:3 | [JsonObject] {module: ...} | tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | semmle.order | 4 | +| tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | tsconfig.json:5:13:5:17 | [JsonString] "dom" | semmle.label | 0 | +| tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | tsconfig.json:5:13:5:17 | [JsonString] "dom" | semmle.order | 0 | +| tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | tsconfig.json:5:20:5:27 | [JsonString] "esnext" | semmle.label | 1 | +| tsconfig.json:5:12:5:28 | [JsonArray] ["dom", ...] | tsconfig.json:5:20:5:27 | [JsonString] "esnext" | semmle.order | 1 | +| tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | tsconfig.json:7:24:7:29 | [JsonString] ".ios" | semmle.label | 0 | +| tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | tsconfig.json:7:24:7:29 | [JsonString] ".ios" | semmle.order | 0 | +| tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | tsconfig.json:7:32:7:33 | [JsonString] "" | semmle.label | 1 | +| tsconfig.json:7:23:7:34 | [JsonArray] [".ios", ...] | tsconfig.json:7:32:7:33 | [JsonString] "" | semmle.order | 1 | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | tst.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | 1 | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | tst.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.order | 1 | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | tst.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | 2 | diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/Imports.expected b/javascript/ql/test/library-tests/frameworks/babel/root-import/Imports.expected deleted file mode 100644 index ee3f8cd7aaf5..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/Imports.expected +++ /dev/null @@ -1,9 +0,0 @@ -| tst1/index.js:1:1:1:20 | import f from '~/a'; | tst1/a.js:1:1:2:0 | | -| tst1/nested/tst.js:1:1:1:20 | import f from '~/a'; | tst1/a.js:1:1:2:0 | | -| tst2/index.js:1:1:1:23 | import ... /b.js'; | tst2/src/js/b.js:1:1:2:0 | | -| tst2/index.js:2:1:2:20 | import f from '#/a'; | tst1/a.js:1:1:2:0 | | -| tst2/index.js:2:1:2:20 | import f from '#/a'; | tst1/index.js:1:1:2:0 | | -| tst3/index.js:1:1:1:30 | import ... /b.js'; | tst3/src/b.js:1:1:2:0 | | -| tst4/index.js:1:1:1:23 | import ... /b.js'; | tst4/src/js/b.js:1:1:2:0 | | -| tst4/index.js:2:1:2:20 | import f from '#/a'; | tst1/a.js:1:1:2:0 | | -| tst4/index.js:2:1:2:20 | import f from '#/a'; | tst1/index.js:1:1:2:0 | | diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/Imports.ql b/javascript/ql/test/library-tests/frameworks/babel/root-import/Imports.ql deleted file mode 100644 index 343775bedc03..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/Imports.ql +++ /dev/null @@ -1,4 +0,0 @@ -import javascript - -from Import i -select i, i.getImportedModule() diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/index.js b/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/index.js deleted file mode 100644 index cd74a094e539..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/index.js +++ /dev/null @@ -1 +0,0 @@ -import f from '~/a'; diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/nested/tst.js b/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/nested/tst.js deleted file mode 100644 index cd74a094e539..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst1/nested/tst.js +++ /dev/null @@ -1 +0,0 @@ -import f from '~/a'; diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/index.js b/javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/index.js deleted file mode 100644 index 1253ec4afd8b..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst2/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import g from '~/b.js'; -import f from '#/a'; diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/index.js b/javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/index.js deleted file mode 100644 index ef05072a9a6b..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst3/index.js +++ /dev/null @@ -1 +0,0 @@ -import greeting from '~/b.js'; diff --git a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/index.js b/javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/index.js deleted file mode 100644 index 1253ec4afd8b..000000000000 --- a/javascript/ql/test/library-tests/frameworks/babel/root-import/tst4/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import g from '~/b.js'; -import f from '#/a'; diff --git a/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/UnresolvableImport.expected b/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/UnresolvableImport.expected index c2f977b89758..deb6b931aaaf 100644 --- a/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/UnresolvableImport.expected +++ b/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/UnresolvableImport.expected @@ -1,3 +1,2 @@ | src/tst.js:2:1:2:16 | require('other') | Module other cannot be resolved, and is not declared as a dependency in $@. | src/package.json:1:1:15:1 | {\\n "na ... "\\n }\\n} | package.json | -| src/tst.js:7:1:7:14 | require('mod') | Module mod cannot be resolved, and is not declared as a dependency in $@. | src/package.json:1:1:15:1 | {\\n "na ... "\\n }\\n} | package.json | | src/tst.js:8:1:8:21 | require ... lared') | Module undeclared cannot be resolved, and is not declared as a dependency in $@. | src/package.json:1:1:15:1 | {\\n "na ... "\\n }\\n} | package.json | diff --git a/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/sub/node_modules/mod/package.json b/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/sub/node_modules/mod/package.json new file mode 100644 index 000000000000..235a7607a8d6 --- /dev/null +++ b/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/sub/node_modules/mod/package.json @@ -0,0 +1,3 @@ +{ + "name": "mod" +} diff --git a/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/tst.js b/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/tst.js index b5d9982bd4e0..e1d19155b549 100644 --- a/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/tst.js +++ b/javascript/ql/test/query-tests/NodeJS/UnresolvableImport/src/tst.js @@ -4,8 +4,8 @@ require('foo'); // OK - declared as a dependency require('bar/sub'); // OK - 'bar' declared as a dependency require('./local'); // OK - local import require('/global'); // OK - global import -require('mod'); // $ Alert +require('mod'); // $ MISSING: Alert // this is resolved due to the package.json file named "mod", but Node.js would not find it require('undeclared'); // $ Alert require('jade!./template.jade'); // OK - WebPack loader require('imports?$=jquery!./m.js'); // OK - WebPack shim -require('react'); // OK - peer dependency \ No newline at end of file +require('react'); // OK - peer dependency diff --git a/javascript/ql/test/query-tests/NodeJS/UnusedDependency/UnusedDependency.expected b/javascript/ql/test/query-tests/NodeJS/UnusedDependency/UnusedDependency.expected index 766f71c09779..b5ffdcb40d89 100644 --- a/javascript/ql/test/query-tests/NodeJS/UnusedDependency/UnusedDependency.expected +++ b/javascript/ql/test/query-tests/NodeJS/UnusedDependency/UnusedDependency.expected @@ -3,5 +3,6 @@ | src/package.json:24:63:24:65 | "*" | Unused dependency 'not-really-used-in-install-script-because-of-substring'. | | src/package.json:28:28:28:30 | "*" | Unused dependency 'used-in-test-script'. | | src/package.json:29:30:29:32 | "*" | Unused dependency 'used-in-custom-script'. | +| src/package.json:30:56:30:58 | "*" | Unused dependency 'used-in-require-with-exclamation-mark-separator'. | | src/package.json:32:28:32:30 | "*" | Unused dependency 'used-in-mjs-require'. | | src/package.json:36:48:36:50 | "*" | Unused dependency 'used-in-dynamic-template-require-string'. | diff --git a/shared/util/codeql/util/FileSystem.qll b/shared/util/codeql/util/FileSystem.qll index 8eb372e0c24a..2b120faaacea 100644 --- a/shared/util/codeql/util/FileSystem.qll +++ b/shared/util/codeql/util/FileSystem.qll @@ -221,57 +221,154 @@ module Make { /** Provides logic related to `Folder`s. */ module Folder { + pragma[nomagic] + private Container getAChildContainer(Container c, string baseName) { + result = c.getAChildContainer() and + baseName = result.getBaseName() + } + /** Holds if `relativePath` needs to be appended to `f`. */ signature predicate shouldAppendSig(Folder f, string relativePath); /** Provides the `append` predicate for appending a relative path onto a folder. */ module Append { + private module Config implements ResolveSig { + predicate shouldResolve(Container c, string name) { shouldAppend(c, name) } + } + + predicate append = Resolve::resolve/2; + } + + /** + * Signature for modules to pass to `Resolve`. + */ + signature module ResolveSig { + /** + * Holds if `path` should be resolved to a file or folder, relative to `base`. + */ + predicate shouldResolve(Container base, string path); + + /** + * Gets an additional file or folder to consider a child of `base`. + */ + default Container getAnAdditionalChild(Container base, string name) { none() } + + /** + * Holds if `component` may be treated as `.` if it does not match a child. + */ + default predicate isOptionalPathComponent(string component) { none() } + + /** + * Holds if globs should be interpreted in the paths being resolved. + * + * The following types of globs are supported: + * - `*` (matches any child) + * - `**` (matches any child recursively) + * - Complex patterns like `foo-*.txt` are also supported + */ + default predicate allowGlobs() { none() } + + /** + * Gets an alternative path segment to try if `segment` did not match a child. + * + * The motivating use-case is to map compiler-generated file names back to their sources files, + * for example, `foo.min.js` could be mapped to `foo.ts`. + */ + bindingset[segment] + default string rewritePathSegment(string segment) { none() } + } + + /** + * Provides a mechanism for resolving file paths relative to a given directory. + */ + module Resolve { + private import Config + pragma[nomagic] - private string getComponent(string relativePath, int i) { - shouldAppend(_, relativePath) and - result = relativePath.replaceAll("\\", "/").regexpFind("[^/]+", i, _) + private string getPathSegment(string path, int n) { + shouldResolve(_, path) and + result = path.replaceAll("\\", "/").splitAt("/", n) } - private int getNumberOfComponents(string relativePath) { - result = strictcount(int i | exists(getComponent(relativePath, i)) | i) - or - relativePath = "" and - result = 0 + pragma[nomagic] + private string getPathSegmentAsGlobRegexp(string segment) { + allowGlobs() and + segment = getPathSegment(_, _) and + segment.matches("%*%") and + not segment = ["*", "**"] and // these are special-cased + result = segment.regexpReplaceAll("[^a-zA-Z0-9*]", "\\\\$0").replaceAll("*", ".*") } pragma[nomagic] - private Container getAChildContainer(Container c, string baseName) { - result = c.getAChildContainer() and - baseName = result.getBaseName() + private int getNumPathSegment(string path) { + result = strictcount(int n | exists(getPathSegment(path, n))) + } + + private Container getChild(Container base, string name) { + result = getAChildContainer(base, name) + or + result = getAnAdditionalChild(base, name) } pragma[nomagic] - private Container appendStep(Folder f, string relativePath, int i) { - i = -1 and - shouldAppend(f, relativePath) and - result = f + private Container resolve(Container base, string path, int n) { + shouldResolve(base, path) and n = 0 and result = base + or + exists(Container current, string segment | + current = resolve(base, path, n - 1) and + segment = getPathSegment(path, n - 1) + | + result = getChild(current, segment) + or + segment = [".", ""] and + result = current + or + segment = ".." and + result = current.getParentContainer() + or + not exists(getChild(current, segment)) and + ( + isOptionalPathComponent(segment) and + result = current + or + result = getChild(current, rewritePathSegment(segment)) + ) + or + allowGlobs() and + ( + segment = "*" and + result = getChild(current, _) + or + segment = "**" and // allow empty match + result = current + or + exists(string name | + result = getChild(current, name) and + name.regexpMatch(getPathSegmentAsGlobRegexp(segment)) + ) + ) + ) or - exists(Container mid, string comp | - mid = appendStep(f, relativePath, i - 1) and - comp = getComponent(relativePath, i) and - if comp = ".." - then result = mid.getParentContainer() - else - if comp = "." - then result = mid - else result = getAChildContainer(mid, comp) + exists(Container current, string segment | + current = resolve(base, path, n) and + segment = getPathSegment(path, n) + | + // Follow child without advancing 'n' + allowGlobs() and + segment = "**" and + result = getChild(current, _) ) } /** - * Gets the file or folder obtained by appending `relativePath` onto `f`. + * Gets the file or folder that `path` resolves to when resolved from `base`. + * + * Only has results for the `(base, path)` pairs provided by `shouldResolve` + * in the instantiation of this module. */ pragma[nomagic] - Container append(Folder f, string relativePath) { - exists(int last | - last = getNumberOfComponents(relativePath) - 1 and - result = appendStep(f, relativePath, last) - ) + Container resolve(Container base, string path) { + result = resolve(base, path, getNumPathSegment(path)) } } }