-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Preload modules from require calls
#5319
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
base: master
Are you sure you want to change the base?
Conversation
9fdb1fd to
9d8a30f
Compare
4d232bb to
2142f1a
Compare
9d8a30f to
2d2a426
Compare
2d2a426 to
b7254c6
Compare
Now when a loaded js module has a `require` function call its argument (if string) will be loaded as a module and we will repeat the process. This should load all modules, and more importantly will help with finding if any missing modules can not be loaded. This is very likely not a good idea in general, but is what esbuild is doing currently if auto extension resolution is enabled. So this is copying this behaviour. It is likely that this will be disabled in the future (v2) and might need or not need additional testing.
b7254c6 to
4d2e6d2
Compare
js/modules/resolution.go
Outdated
| // TODO(@mstoykov): put this behind a flag | ||
| if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems not as easy as I was hoping, and I would like to first confirm that we will want to have a flag to disable this, And also if we agree to disable this in v2, as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we guarantee it somewhere? If we haven't provided any guarantee for it then why we do not stop supporting it right now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not against it. BUt we do not currently have any data on how used it is. And I am not certain we have no place where we give this as an example or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mstoykov If I understand it correctly, you mean the pre-loading or required modules during the dependency analysis, right? Also, I understand we support require at execution time. So, if we stop analyzing the required modules, wouldn't it create inconsistent behavior by not recognizing the dependencies from those modules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require is runtime/exucution time constract.
This code similar to what esbuild does, is going through the code and looking at anything that seems to be a require call and if the arguments is a string literal - great, we preload it.
This (as mentione in #5325) isn't ... very reliable to begin wiht. For simple cases it definitely does work, but it both doesn't work for cases where the argument is a variable (actaully not uncommon for complex scripts IME) and it also definitely over finds stuff IME both now and in esbuild implementation. The esbuild technically does better, by dropping "dead" code ... but even that seems to be very uncosistant in me experimentation. Where for some simple cases it works and for a slightly different one it apperantly can not drop the code.
Also obviously you can put require behind a if statement in which case it is even more of ... should this be imported at all.
But all that aside - require and commonJS are not standard and hopefully ... they will not be used a lot.
So IMO we should not have this hacky ways to have and instead should ask people to use ESM.
This btw is also going to have to be true for dynamic imports when we support them ... and I do not think doing that is good idea too.
codebien
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current version LGTM, but if we don't want to support it then I think we should drop it now instead of waiting for v2.
| } | ||
| // TODO(@mstoykov): put this behind a flag | ||
| if err != nil { | ||
| potentialRequireCalls := findRequireFunctionInAST(prg.Body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| potentialRequireCalls := findRequireFunctionInAST(prg.Body) | |
| requireCalls := findRequireFunctionInAST(prg.Body) |
Why do we call them potential? I guess they are require calls or not. What did I miss?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here potential means htat ... they might not actually get called - they are part of the code, but this doesn't analyse what is called, just what code exists.
js/modules/resolution.go
Outdated
| // TODO(@mstoykov): put this behind a flag | ||
| if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we guarantee it somewhere? If we haven't provided any guarantee for it then why we do not stop supporting it right now?
| return result | ||
| } | ||
|
|
||
| func findRequireFunctionInStatement(i ast.Statement) []string { //nolint:cyclop,funlen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| func findRequireFunctionInStatement(i ast.Statement) []string { //nolint:cyclop,funlen | |
| // findRequireFunctionInStatement finds ... | |
| func findRequireFunctionInStatement(i ast.Statement) []string { //nolint:cyclop,funlen |
Co-authored-by: Ivan <2103732+codebien@users.noreply.github.com>
Reimplement auto extension resolution without using esbuild or k6deps, but the internal methods that k6 uses to load modules. This removes the need for a separate project to have the same way to parse and understand imports and also double parsing everything. Also fixing a number of bugs coming from this and removes the maintenance burden to keep both projects align. It also fixes problems with paths on windows as it is now using the internal handling everywhere. Makes use directives actually only work at the beginning of files, but also for all files. It does now not work with `require` calls, at least until/if #5319 is merged. Correctly propagates exit codes from sub processes up. Fixes #5127 FIxes #5176 Fixes #5104 Closes #5102 Likely some updates to every other issue around automatic extension resolution as well. Co-authored-by: Ivan <2103732+codebien@users.noreply.github.com> Co-authored-by: Théo Crevon <oleiade@users.noreply.github.com>
What?
Now when a loaded js module has a
requirefunction call its arguTo be disabment (if string) will be loaded as a module and we will repeat the process.This should load all modules, and more importantly will help with finding if any missing modules can not be loaded.
This is very likely not a good idea in general, but is what esbuild is doing currently if auto extension resolution is enabled. So this is copying this behaviour.
It is likely that this will be disabled in the future (v2) and might need or not need additional testing.
Why?
This is what currently happens in esbuild+k6deps dependancy analysis so this just copies it
Checklist
make check) and all pass.Checklist: Documentation (only for k6 maintainers and if relevant)
Please do not merge this PR until the following items are filled out.
Related PR(s)/Issue(s)
#5320 combines this and #5240 to reimplement auto extension resolution without esbuild and k6deps.
Issue/discussion for stopping this in v2 #5325