diff --git a/js/initcontext.go b/js/initcontext.go index 34c4a65ec5c..cad1e15a339 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -45,13 +45,14 @@ type InitContext struct { pwd *url.URL // Cache of loaded programs and files. - programs map[string]programWithSource + programs map[string]programWithSource + exportsCache map[string]goja.Value compatibilityMode lib.CompatibilityMode logger logrus.FieldLogger - modules map[string]interface{} + moduleRegistry map[string]interface{} } // NewInitContext creates a new initcontext with the provided arguments @@ -66,7 +67,8 @@ func NewInitContext( programs: make(map[string]programWithSource), compatibilityMode: compatMode, logger: logger, - modules: getJSModules(), + moduleRegistry: getJSModules(), + exportsCache: make(map[string]goja.Value), moduleVUImpl: &moduleVUImpl{ ctx: context.Background(), runtime: rt, @@ -92,14 +94,20 @@ func newBoundInitContext(base *InitContext, vuImpl *moduleVUImpl) *InitContext { programs: programs, compatibilityMode: base.compatibilityMode, + exportsCache: make(map[string]goja.Value), logger: base.logger, - modules: base.modules, + moduleRegistry: base.moduleRegistry, moduleVUImpl: vuImpl, } } // Require is called when a module/file needs to be loaded by a script -func (i *InitContext) Require(arg string) goja.Value { +func (i *InitContext) Require(arg string) (export goja.Value) { + var ok bool + if export, ok = i.exportsCache[arg]; ok { + return export + } + defer func() { i.exportsCache[arg] = export }() switch { case arg == "k6", strings.HasPrefix(arg, "k6/"): // Builtin or external modules ("k6", "k6/*", or "k6/x/*") are handled @@ -144,7 +152,7 @@ func toESModuleExports(exp modules.Exports) interface{} { } func (i *InitContext) requireModule(name string) (goja.Value, error) { - mod, ok := i.modules[name] + mod, ok := i.moduleRegistry[name] if !ok { return nil, fmt.Errorf("unknown module: %s", name) } diff --git a/js/initcontext_test.go b/js/initcontext_test.go index b1a4a0023a5..30190db82bd 100644 --- a/js/initcontext_test.go +++ b/js/initcontext_test.go @@ -687,3 +687,34 @@ export default function () { // likely settings in the transpilers require.Equal(t, "cool is cool\n\tat webpack:///./test1.ts:2:4(2)\n\tat r (webpack:///./test1.ts:5:4(3))\n\tat file:///script.js:4:2(4)\n\tat native\n", exception.String()) } + +func TestImportModificationsAreConsistentBetweenFiles(t *testing.T) { + t.Parallel() + logger := testutils.NewLogger(t) + fs := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fs, "/notk6.js", []byte(`export default {group}; function group() {}`), 0o644)) + require.NoError(t, afero.WriteFile(fs, "/instrument.js", []byte(` + import k6 from "k6"; + k6.newKey = 5; + k6.group = 3; + + import notk6 from "./notk6.js"; + notk6.group = 3; + notk6.newKey = 5; + `), 0o644)) + + b, err := getSimpleBundle(t, "/script.js", ` + import k6 from "k6"; + import notk6 from "./notk6.js"; + import "./instrument.js"; + if (k6.newKey != 5) { throw "k6.newKey is wrong "+ k6.newKey} + if (k6.group != 3) { throw "k6.group is wrong "+ k6.group} + if (notk6.newKey != 5) { throw "notk6.newKey is wrong "+ notk6.newKey} + if (notk6.group != 3) { throw "notk6.group is wrong "+ notk6.group} + export default () => { throw "this shouldn't be ran" } +`, fs) + require.NoError(t, err, "bundle error") + + _, err = b.Instantiate(logger, 0) + require.NoError(t, err) +}