-
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?
Changes from all commits
3fe2dcc
4d2e6d2
e67feb7
6a5d983
b931037
632e168
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package modules | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/grafana/sobek" | ||
| "github.com/grafana/sobek/parser" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestRequireCalls(t *testing.T) { | ||
| t.Parallel() | ||
| ts := []struct { | ||
| script string | ||
| list []string | ||
| }{ | ||
| { | ||
| script: `require("something")`, | ||
| list: []string{"something"}, | ||
| }, | ||
| { | ||
| script: `require("something"); require("else")`, | ||
| list: []string{"something", "else"}, | ||
| }, | ||
| { | ||
| script: `function a() { require("something"); } require("else")`, | ||
| list: []string{"something", "else"}, | ||
| }, | ||
| { | ||
| script: `export function a () { require("something"); } require("else")`, | ||
| list: []string{"something", "else"}, | ||
| }, | ||
| { | ||
| script: `export const a = require("something"); require("else")`, | ||
| list: []string{"something", "else"}, | ||
| }, | ||
| { | ||
| script: `var a = require("something"); require("else")`, | ||
| list: []string{"something", "else"}, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range ts { | ||
| t.Run(test.script, func(t *testing.T) { | ||
| t.Parallel() | ||
| a, err := sobek.Parse("script", test.script, parser.IsModule) | ||
| require.NoError(t, err) | ||
|
|
||
| list := findRequireFunctionInAST(a.Body) | ||
| require.EqualValues(t, test.list, list) | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -37,6 +37,8 @@ type ModuleResolver struct { | |||||
| base *url.URL | ||||||
| usage *usage.Usage | ||||||
| logger logrus.FieldLogger | ||||||
|
|
||||||
| experimentalPreloadRequire bool | ||||||
| } | ||||||
|
|
||||||
| // NewModuleResolver returns a new module resolution instance that will resolve. | ||||||
|
|
@@ -58,6 +60,14 @@ func NewModuleResolver( | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| // SetExperimentalRequirePreload sets whether or not to preload `require` calls it can detect in each file as it is | ||||||
| // doing the standard ESM resolution. This helps with finding `require` calls before running the code. | ||||||
| // This especially relevant for Auto Extension Resolution which otherwise won't take `require` calls into account. | ||||||
| // Deprecated: this is only available until k6 v2 | ||||||
| func (mr *ModuleResolver) SetExperimentalRequirePreload(enable bool) { | ||||||
| mr.experimentalPreloadRequire = enable | ||||||
| } | ||||||
|
|
||||||
| func (mr *ModuleResolver) resolveSpecifier(basePWD *url.URL, arg string) (*url.URL, error) { | ||||||
| specifier, err := loader.Resolve(basePWD, arg) | ||||||
| if err != nil { | ||||||
|
|
@@ -125,6 +135,18 @@ func (mr *ModuleResolver) resolveLoaded(basePWD *url.URL, arg string, data []byt | |||||
| } else { | ||||||
| mod, err = cjsModuleFromString(prg) | ||||||
| } | ||||||
| if err == nil && mr.experimentalPreloadRequire { | ||||||
| potentialRequireCalls := findRequireFunctionInAST(prg.Body) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Why do we call them
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here |
||||||
| for _, requireArg := range potentialRequireCalls { | ||||||
| _, requireErr := mr.resolve(basePWD, requireArg) | ||||||
| if requireErr != nil { | ||||||
| mr.logger.WithError(requireErr).Debugf("failed preloading %q call for %q", "require", requireArg) | ||||||
| } | ||||||
| if err := mr.usage.Uint64("usage/requirePreload", 1); err != nil { | ||||||
mstoykov marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| mr.logger.WithError(err).Warn("couldn't report require preloading usage for " + requireArg) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| mr.reverse[mod] = specifier | ||||||
| mr.cache[specifier.String()] = moduleCacheElement{mod: mod, err: err} | ||||||
| return mod, err | ||||||
|
|
||||||
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.