From 449e697761ed8a1d4839e30dfaef5d802105c3a1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 16 Aug 2022 11:44:30 +0200 Subject: [PATCH] JS: Handle nested conditions in "exports" section --- .../javascript/NodeModuleResolutionImpl.qll | 43 +++++++++++++------ .../test/library-tests/NPM/src/package.json | 2 +- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll index 475f0888541..7660d374216 100644 --- a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll +++ b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll @@ -147,30 +147,22 @@ private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] } */ class MainModulePath extends PathExpr, @json_string { PackageJson pkg; - string relativePath; MainModulePath() { - relativePath = "." and this = pkg.getPropValue(["main", "module"]) or - // { "exports": "path" } is sugar for { "exports": { ".": "path" }} - relativePath = "." and - this = pkg.getPropValue("exports") - or - exists(JsonValue val | val = pkg.getPropValue("exports").getPropValue(relativePath) | - // Either specified directly as a string: { "./path": "./path.js" } - this = val - or - // Or by module type: { "./path": { "require": "./path.cjs", ... }} - this = val.getPropValue(_) - ) + this = getAPartOfExportsSection(pkg) } /** Gets the `package.json` file in which this path occurs. */ PackageJson getPackageJson() { result = pkg } /** Gets the relative path under which this is exported, usually starting with a `.`. */ - string getRelativePath() { result = relativePath } + string getRelativePath() { + result = getExportRelativePath(this) + or + not exists(getExportRelativePath(this)) and result = "." + } /** DEPRECATED: Alias for getPackageJson */ deprecated PackageJSON getPackageJSON() { result = getPackageJson() } @@ -183,6 +175,29 @@ class MainModulePath extends PathExpr, @json_string { } } +private JsonValue getAPartOfExportsSection(PackageJson pkg) { + result = pkg.getPropValue("exports") + or + result = getAPartOfExportsSection(pkg).getPropValue(_) +} + +/** Gets the text of one of the conditions or paths enclosing the given `part` of an `exports` section. */ +private string getAnEnclosingExportProperty(JsonValue part) { + exists(JsonObject parent, string prop | + parent = getAPartOfExportsSection(_) and + part = parent.getPropValue(prop) + | + result = prop + or + result = getAnEnclosingExportProperty(parent) + ) +} + +private string getExportRelativePath(JsonValue part) { + result = getAnEnclosingExportProperty(part) and + result.matches(".%") +} + module MainModulePath { /** Gets the path to the main entry point of `pkg`. */ MainModulePath of(PackageJson pkg) { result = of(pkg, ".") } diff --git a/javascript/ql/test/library-tests/NPM/src/package.json b/javascript/ql/test/library-tests/NPM/src/package.json index 094bcc81bea..c5104a88521 100644 --- a/javascript/ql/test/library-tests/NPM/src/package.json +++ b/javascript/ql/test/library-tests/NPM/src/package.json @@ -17,4 +17,4 @@ "peerDependencies": { "tea": "2.x" } -} \ No newline at end of file +}