Skip to content

JS: Support sub modules #12975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 23, 2023
Merged

JS: Support sub modules #12975

merged 11 commits into from
May 23, 2023

Conversation

tyage
Copy link
Contributor

@tyage tyage commented Apr 30, 2023

Some npm packages have sub modules which have path suffix after the module name.
For example, @github/webauthn-json has sub module @github/webauthn-json/extended and its structure looks like this.

node_modules/
  - @github/webauthn-json/
    - package.json
    - extended/
      - package.json

I updated NPM.qll so that the CodeQL finds these modules.

https://nodejs.org/api/modules.html#loading-from-node_modules-folders

It is possible to require specific files or sub modules distributed with a module by including a path suffix after the module name. For instance require('example-module/path/to/file') would resolve path/to/file relative to where example-module is located. The suffixed path follows the same module resolution semantics.

@erik-krogh erik-krogh self-assigned this May 1, 2023
Copy link
Contributor

@erik-krogh erik-krogh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly OK 👍

But I got a few comments.

parentPackageName = parentPackage.getPropStringValue("name") and
parentDir.indexOf("node_modules") != -1 and
currentDir != parentDir and
currentDir.indexOf(parentDir) = 0 and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are doing a string-comparison here, and I'm not a fan of that.

You could instead drop the .getAbsolutePath(), and then do something like:
parentDir.getAChildContainer+() = currentDir

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!
That looks better!
I refactored the codes here.

@@ -0,0 +1 @@
export default "parent";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a typo in the folder name.
You have named the folder parent-modue instead of parent-module.

I tried to run your test-case locally using node (to confirm that the behavior is correct), and it failed due to the typo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, sorry.
I'll fix it soon.

exists(PackageJson parentPkg, Container currentDir, Container parentDir |
currentDir = this.getJsonFile().getParentContainer() and
parentDir = parentPkg.getJsonFile().getParentContainer() and
parentDir.getParentContainer+().getBaseName() = "node_modules" and
Copy link
Contributor

@erik-krogh erik-krogh May 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parentDir.getParentContainer+().getBaseName() = "node_modules" and
parentDir.getParentContainer().getBaseName() = "node_modules" and

I'm not sure I like that transitive closure.
Your test only covers that the parentDir is directly contained within node_modules, and I'm not sure what happens if parentDir is not directly inside node_modules.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a scoped package such as @babel/core.
In this case pakcage.json located in node_modules/@babel/core/ and there is no package in node_modules/@babel/.
AFAIK, this is the only case that parentDir is not directly inside node_modules.
Do you think it is better to rewrite this code to the following one?

parentDir.getParentContainer().getBaseName() = "node_modules" or parentDir.getParentContainer().getParentContainer().getBaseName() = "node_modules" and

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it is better to rewrite this code to the following one?

Yes, that could work.
And in the .getParentContainer().getParentContainer() case you can check that the packagename of the parentPkg contains "/".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I fixed the code.
Thanks for your review!

Comment on lines 25 to 32
parentPkgName = parentPkg.getPropStringValue("name") and
(
parentDir.getParentContainer().getBaseName() = "node_modules"
or
// Scoped package is located in node_modules/@scope/pkgname
parentDir.getParentContainer().getParentContainer().getBaseName() = "node_modules" and
exists(parentPkgName.indexOf("/"))
) and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parentPkgName = parentPkg.getPropStringValue("name") and
(
parentDir.getParentContainer().getBaseName() = "node_modules"
or
// Scoped package is located in node_modules/@scope/pkgname
parentDir.getParentContainer().getParentContainer().getBaseName() = "node_modules" and
exists(parentPkgName.indexOf("/"))
) and
parentPkgName = parentPkg.getPropStringValue("name") and

On further thought (sorry for the many rounds of feedback).
Do we need to restrict to just packages contained within a node_modules folder?

Monorepos that contain many packages could experience this pattern without a node_module folder being part of it?

Copy link
Contributor Author

@tyage tyage May 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think developers need to define exports property in package.json to access other packages, if they aren't located in node_modules.
I think removing these codes might cause some false positives, but they might be rare case, so we might be able to accept them...?

I looked at some packages and in the case of @github/webauthn-json, they defined exports property so that @github/webauthn-json/extended will work.
This means that the package name is defined independently of the directory structure.

https://github.com/github/webauthn-json/blob/e932b3585fa70b0bd5b5a4012ba7dbad7b0a0d0f/package.json#L18
https://nodejs.org/api/packages.html#exports-sugar

If we remove the exports property, @github/webauthn-json/extended will not work.

$ node -e 'console.log(require("@github/webauthn-json/extended"))'
{
  base64urlToBuffer: [Getter],
...
  supported: [Getter]
}
$ gsed -i 's/exports/foobar/' package.json
$ node -e 'console.log(require("@github/webauthn-json/extended"))'
node:internal/modules/cjs/loader:936
  throw err;
  ^

Error: Cannot find module '@github/webauthn-json/extended'

In https://github.com/keystonejs/keystone, we can call subpath modules like @keystone-6/core/admin-ui/router after npm install.
In this case, npm workspaces put the symlinks in the node_modules directory.

https://github.com/keystonejs/keystone/blob/5ac8ef453/package.json#L109
https://docs.npmjs.com/cli/v7/using-npm/workspaces

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think developers need to define exports property in package.json to access other packages, if they aren't located in node_modules.

We already have some support for "exports" elsewhere.


My thinking was that we most of the time ignore node_modules folder when we parse a project, so your contribution is very rarely relevant.

but they might be rare case, so we might be able to accept them...?

I'll run an evaluation if you try to remove the node_modules restriction.
If the results are noisy (or if the performance goes bad), then we can revert the change again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok!
I committed your suggestion.👌

Copy link
Contributor

@erik-krogh erik-krogh May 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The failing tests (see the CI below) revealed something interesting.

We get some package names that include node_modules.
E.g. the Dependencies.ql test finds a new package-name: test-package/node_modules/bundled-package.
I don't think that was what you meant with this contribution? so maybe filter those out?

I did some evaluations (internal links: one, two), and that looks fine.
Nothing happened in those evaluations, everything is the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see.
I'll filter them out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks fine now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks OK.
I'll run an evaluation on it later, but our system for doing so is having some issues right now.
And I'm on PTO thursday+friday, so it might be until early next week before I can check it out.
Don't worry, I haven't forgotten about your PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
Enjoy your holiday! 😀

Copy link
Contributor

@erik-krogh erik-krogh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

Evaluation (internal link) looks OK.

@erik-krogh erik-krogh merged commit e658177 into github:main May 23, 2023
@tyage tyage deleted the support-sub-modules branch May 23, 2023 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants