Merge pull request #10036 from asgerf/js/exports-handling

JS: More precise handling of "exports"
This commit is contained in:
Asger F 2022-08-15 15:32:00 +02:00 коммит произвёл GitHub
Родитель 16451654db 3c41f28519
Коммит eaf3aa7075
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 58 добавлений и 12 удалений

Просмотреть файл

@ -50,8 +50,23 @@ class PackageJson extends JsonObject {
/** Gets a file for this package. */
string getAFile() { result = this.getFiles().getElementStringValue(_) }
/** Gets the main module of this package. */
string getMain() { result = MainModulePath::of(this).getValue() }
/**
* Gets the main module of this package.
*
* This can be given by the `main` or `module` property, or via the
* `exports` property with the relative path `"."`.
*/
string getMain() { result = this.getExportedPath(".") }
/**
* Gets the path to the file exported with the given relative path.
*
* This can be given by the `exports` property, but also considers `main` and
* `module` paths to be exported under the relative path `"."`.
*/
string getExportedPath(string relativePath) {
result = MainModulePath::of(this, relativePath).getValue()
}
/** Gets the path of a command defined for this package. */
string getBin(string cmd) {
@ -181,6 +196,18 @@ class PackageJson extends JsonObject {
result = min(Module m, int prio | m.getFile() = resolveMainModule(this, prio) | m order by prio)
}
/**
* Gets the module exported under the given relative path.
*
* The main module is considered exported under the path `"."`.
*/
Module getExportedModule(string relativePath) {
relativePath = "." and
result = this.getMainModule()
or
result.getFile() = MainModulePath::of(this, relativePath).resolve()
}
/**
* Gets the `types` or `typings` field of this package.
*/

Просмотреть файл

@ -93,7 +93,7 @@ private string getStem(string name) { result = name.regexpCapture("(.+?)(?:\\.([
* Gets the main module described by `pkg` with the given `priority`.
*/
File resolveMainModule(PackageJson pkg, int priority) {
exists(PathExpr main | main = MainModulePath::of(pkg) |
exists(PathExpr main | main = MainModulePath::of(pkg, ".") |
result = main.resolve() and priority = 0
or
result = tryExtensions(main.resolve(), "index", priority)
@ -142,21 +142,36 @@ File resolveMainModule(PackageJson pkg, int priority) {
private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] }
/**
* A JSON string in a `package.json` file specifying the path of the main
* module of the package.
* 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 {
PackageJson pkg;
string relativePath;
MainModulePath() {
relativePath = "." and
this = pkg.getPropValue(["main", "module"])
or
this = getAJsonChild*(pkg.getPropValue("exports"))
// { "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(_)
)
}
/** 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 }
/** DEPRECATED: Alias for getPackageJson */
deprecated PackageJSON getPackageJSON() { result = getPackageJson() }
@ -168,11 +183,15 @@ class MainModulePath extends PathExpr, @json_string {
}
}
/** Gets the value of a property from the JSON object `obj`. */
private JsonValue getAJsonChild(JsonObject obj) { result = obj.getPropValue(_) }
module MainModulePath {
MainModulePath of(PackageJson pkg) { result.getPackageJson() = pkg }
/** Gets the path to the main entry point of `pkg`. */
MainModulePath of(PackageJson pkg) { result = of(pkg, ".") }
/** Gets the path to the file exported from `pkg` as `relativePath`. */
MainModulePath of(PackageJson pkg, string relativePath) {
result.getPackageJson() = pkg and
result.getRelativePath() = relativePath
}
}
/**
@ -185,7 +204,7 @@ private class FilesPath extends PathExpr, @json_string {
FilesPath() {
this = pkg.getPropValue("files").(JsonArray).getElementValue(_) and
not exists(MainModulePath::of(pkg))
not exists(MainModulePath::of(pkg, _))
}
/** Gets the `package.json` file in which this path occurs. */

Просмотреть файл

@ -52,7 +52,7 @@ private import NodeModuleResolutionImpl as NodeModule
private DataFlow::Node getAValueExportedByPackage() {
// The base case, an export from a named `package.json` file.
result =
getAnExportFromModule(any(PackageJson pack | exists(pack.getPackageName())).getMainModule())
getAnExportFromModule(any(PackageJson pack | exists(pack.getPackageName())).getExportedModule(_))
or
// module.exports.bar.baz = result;
exists(DataFlow::PropWrite write |