treat "files" in a package.json as main modules, if "main" is not present

This commit is contained in:
Erik Krogh Kristensen
2021-04-08 14:42:12 +02:00
parent a9527fd913
commit 30ba69d991
8 changed files with 122 additions and 3 deletions

View File

@@ -51,7 +51,7 @@ class PackageJSON extends JSONObject {
string getAFile() { result = getFiles().getElementStringValue(_) }
/** Gets the main module of this package. */
string getMain() { result = getPropStringValue("main") }
string getMain() { result = MainModulePath::of(this).getValue() }
/** Gets the path of a command defined for this package. */
string getBin(string cmd) {

View File

@@ -80,6 +80,18 @@ File tryExtensions(Folder dir, string basename, int priority) {
)
}
/**
* Gets `name` without a file extension.
* Or `name`, if `name` has no file extension.
*/
bindingset[name]
private string maybeRemoveExtension(string name) {
result = name.regexpCapture("^(.+)\\.[^.]+$", 1)
or
not name.regexpMatch(".+\\..+") and
result = name
}
/**
* Gets the main module described by `pkg` with the given `priority`.
*/
@@ -90,9 +102,10 @@ File resolveMainModule(PackageJSON pkg, int priority) {
result = tryExtensions(main.resolve(), "index", priority)
or
not exists(main.resolve()) and
not exists(main.getExtension()) and
exists(int n | n = main.getNumComponent() |
result = tryExtensions(main.resolveUpTo(n - 1), main.getComponent(n - 1), priority)
result =
tryExtensions(main.resolveUpTo(n - 1), maybeRemoveExtension(main.getComponent(n - 1)),
priority)
)
)
or
@@ -101,6 +114,28 @@ File resolveMainModule(PackageJSON pkg, int priority) {
tryExtensions([folder, folder.getChildContainer(["src", "lib"])], "index",
priority - prioritiesPerCandidate())
)
or
// if there is no main module, then we look for files that are explicitly included in the published package.
exists(PathExpr file |
// `FilesPath` only exists if there is no main module for a given package.
file = FilesPath::of(pkg) and priority = 100 // fixing the priority, because there might be multiple files in the package.
|
result = file.resolve()
or
result = min(int i, File f | f = tryExtensions(file.resolve(), "index", i) | f order by i)
or
// resolve "file.js" to e.g. "file.ts".
not exists(file.resolve()) and
exists(int n | n = file.getNumComponent() |
result =
min(int i, File res |
res =
tryExtensions(file.resolveUpTo(n - 1), maybeRemoveExtension(file.getComponent(n - 1)), i)
|
res order by i
)
)
)
}
/**
@@ -126,3 +161,31 @@ class MainModulePath extends PathExpr, @json_string {
module MainModulePath {
MainModulePath of(PackageJSON pkg) { result.getPackageJSON() = pkg }
}
/**
* A JSON string in a `package.json` file specifying a file that should be included in the published package.
* 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 {
PackageJSON pkg;
FilesPath() {
this = pkg.getPropValue("files").(JSONArray).getElementValue(_) and
not exists(MainModulePath::of(pkg))
}
/** Gets the `package.json` file in which this path occurs. */
PackageJSON getPackageJSON() { result = pkg }
override string getValue() { result = this.(JSONString).getValue() }
override Folder getAdditionalSearchRoot(int priority) {
priority = 0 and
result = pkg.getFile().getParentContainer()
}
}
private module FilesPath {
FilesPath of(PackageJSON pkg) { result.getPackageJSON() = pkg }
}

View File

@@ -246,6 +246,18 @@ nodes
| lib/lib.js:482:40:482:43 | name |
| lib/lib.js:483:30:483:33 | name |
| lib/lib.js:483:30:483:33 | name |
| lib/subLib2/compiled-file.ts:3:26:3:29 | name |
| lib/subLib2/compiled-file.ts:3:26:3:29 | name |
| lib/subLib2/compiled-file.ts:4:25:4:28 | name |
| lib/subLib2/compiled-file.ts:4:25:4:28 | name |
| lib/subLib2/special-file.js:3:28:3:31 | name |
| lib/subLib2/special-file.js:3:28:3:31 | name |
| lib/subLib2/special-file.js:4:22:4:25 | name |
| lib/subLib2/special-file.js:4:22:4:25 | name |
| lib/subLib3/my-file.ts:3:28:3:31 | name |
| lib/subLib3/my-file.ts:3:28:3:31 | name |
| lib/subLib3/my-file.ts:4:22:4:25 | name |
| lib/subLib3/my-file.ts:4:22:4:25 | name |
| lib/subLib/index.js:3:28:3:31 | name |
| lib/subLib/index.js:3:28:3:31 | name |
| lib/subLib/index.js:4:22:4:25 | name |
@@ -546,6 +558,18 @@ edges
| lib/lib.js:482:40:482:43 | name | lib/lib.js:483:30:483:33 | name |
| lib/lib.js:482:40:482:43 | name | lib/lib.js:483:30:483:33 | name |
| lib/lib.js:482:40:482:43 | name | lib/lib.js:483:30:483:33 | name |
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
| lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name |
| lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name |
| lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name |
| lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name |
| lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name |
| lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name |
| lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name |
| lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name |
| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
| lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name |
@@ -624,5 +648,8 @@ edges
| lib/lib.js:447:13:447:28 | "rm -rf " + name | lib/lib.js:446:20:446:23 | name | lib/lib.js:447:25:447:28 | name | $@ based on $@ is later used in $@. | lib/lib.js:447:13:447:28 | "rm -rf " + name | String concatenation | lib/lib.js:446:20:446:23 | name | library input | lib/lib.js:447:3:447:29 | asyncEx ... + name) | shell command |
| lib/lib.js:478:27:478:46 | config.installedPath | lib/lib.js:477:33:477:38 | config | lib/lib.js:478:27:478:46 | config.installedPath | $@ based on $@ is later used in $@. | lib/lib.js:478:27:478:46 | config.installedPath | Path concatenation | lib/lib.js:477:33:477:38 | config | library input | lib/lib.js:479:12:479:20 | exec(cmd) | shell command |
| lib/lib.js:483:13:483:33 | ' my na ... + name | lib/lib.js:482:40:482:43 | name | lib/lib.js:483:30:483:33 | name | $@ based on $@ is later used in $@. | lib/lib.js:483:13:483:33 | ' my na ... + name | String concatenation | lib/lib.js:482:40:482:43 | name | library input | lib/lib.js:485:2:485:20 | cp.exec(cmd + args) | shell command |
| lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | $@ based on $@ is later used in $@. | lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | String concatenation | lib/subLib2/compiled-file.ts:3:26:3:29 | name | library input | lib/subLib2/compiled-file.ts:4:5:4:29 | cp.exec ... + name) | shell command |
| lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/subLib2/special-file.js:3:28:3:31 | name | library input | lib/subLib2/special-file.js:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | String concatenation | lib/subLib3/my-file.ts:3:28:3:31 | name | library input | lib/subLib3/my-file.ts:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/subLib/index.js:4:10:4:25 | "rm -rf " + name | lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/subLib/index.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/subLib/index.js:3:28:3:31 | name | library input | lib/subLib/index.js:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | $@ based on $@ is later used in $@. | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/subLib/index.js:7:32:7:35 | name | library input | lib/subLib/index.js:8:2:8:26 | cp.exec ... + name) | shell command |

View File

@@ -0,0 +1,5 @@
var cp = require("child_process")
export default function (name) {
cp.exec("rm -rf " + name); // NOT OK - the "files" directory points to this file.
}

View File

@@ -0,0 +1,9 @@
{
"name": "my-sub-lib2",
"version": "0.0.7",
"files": [
"special-file.js",
"compiled-file.js",
"compiled-file"
]
}

View File

@@ -0,0 +1,5 @@
var cp = require("child_process")
module.exports = function (name) {
cp.exec("rm -rf " + name); // NOT OK - the "files" directory points to this file.
};

View File

@@ -0,0 +1,5 @@
var cp = require("child_process")
module.exports = function (name) {
cp.exec("rm -rf " + name); // NOT OK - functions exported as part of a submodule are also flagged.
};

View File

@@ -0,0 +1,5 @@
{
"name": "my-sub-lib",
"version": "0.0.7",
"main": "./my-file.js"
}