diff --git a/js/module_loading_test.go b/js/module_loading_test.go index 2b2ea0a64ca..060eb4c4e50 100644 --- a/js/module_loading_test.go +++ b/js/module_loading_test.go @@ -617,30 +617,69 @@ func TestDefaultNamedExports(t *testing.T) { func TestStarImport(t *testing.T) { t.Parallel() - fs := fsext.NewMemMapFs() - err := writeToFs(fs, map[string]any{ - "/commonjs_file.js": `exports.something = 5;`, + + t.Run("esm_spec", func(t *testing.T) { + t.Parallel() + fs := fsext.NewMemMapFs() + err := writeToFs(fs, map[string]any{ + "/commonjs_file.js": `exports.something = 5;`, + }) + require.NoError(t, err) + + r1, err := getSimpleRunner(t, "/script.js", ` + import * as cjs from "./commonjs_file.js"; // commonjs + import * as k6 from "k6"; // "new" go module + // TODO: test with basic go module maybe + + if (cjs.something != 5) { + throw "cjs.something has wrong value" + cjs.something; + } + if (typeof k6.sleep != "function") { + throw "k6.sleep has wrong type" + typeof k6.sleep; + } + export default () => {} + `, fs, lib.RuntimeOptions{CompatibilityMode: null.StringFrom("extended")}) + require.NoError(t, err) + + arc := r1.MakeArchive() + _, err = getSimpleArchiveRunner(t, arc) + require.NoError(t, err) }) - require.NoError(t, err) - r1, err := getSimpleRunner(t, "/script.js", ` - import * as cjs from "./commonjs_file.js"; // commonjs - import * as k6 from "k6"; // "new" go module - // TODO: test with basic go module maybe + t.Run("default_to_namespaced_object", func(t *testing.T) { + t.Parallel() - if (cjs.something != 5) { - throw "cjs.something has wrong value" + cjs.something; - } - if (typeof k6.sleep != "function") { - throw "k6.sleep has wrong type" + typeof k6.sleep; - } - export default () => {} - `, fs, lib.RuntimeOptions{CompatibilityMode: null.StringFrom("extended")}) - require.NoError(t, err) + r1, err := getSimpleRunner(t, "/script.js", ` + import http from "k6/http"; + import * as httpNO from "k6/http"; - arc := r1.MakeArchive() - _, err = getSimpleArchiveRunner(t, arc) - require.NoError(t, err) + const httpKeys = Object.keys(http); + const httpNOKeys = Object.keys(httpNO); + + // 1. Check if both have the same number of properties + if (httpKeys.length !== httpNOKeys.length) { + throw "Objects have a different number of properties."; + } + + // 2. Check if all properties match + for (const key of httpKeys) { + if (!Object.prototype.hasOwnProperty.call(httpNO, key)) { + throw `+"`Property ${key} is missing in the second object.`"+`; + } + + if (http[key] !== httpNO[key]) { + throw `+"`Property ${key} does not match between the objects.`"+`; + } + } + + export default () => {} + `, lib.RuntimeOptions{CompatibilityMode: null.StringFrom("extended")}) + require.NoError(t, err) + + arc := r1.MakeArchive() + _, err = getSimpleArchiveRunner(t, arc) + require.NoError(t, err) + }) } func TestIndirectExportDefault(t *testing.T) { diff --git a/js/modules/gomodule.go b/js/modules/gomodule.go index 8193b2fdde4..dea074af3ec 100644 --- a/js/modules/gomodule.go +++ b/js/modules/gomodule.go @@ -25,10 +25,20 @@ func (gm *goModule) Instantiate(rt *sobek.Runtime) (sobek.CyclicModuleInstance, if gm.exportedNames == nil { named := mi.Exports().Named - gm.exportedNames = make([]string, len(named)) - for name := range named { - gm.exportedNames = append(gm.exportedNames, name) + if named == nil && mi.Exports().Default != nil { + // If named is nil but default is defined, then try to work with + // default and extract the names of the object's properties. This + // behavior isn't ESM compatible, but we do want to allow defaults to + // be imported as namespaced object, which is also how node works. + obj := rt.ToValue(mi.Exports().Default).ToObject(rt) + gm.exportedNames = obj.GetOwnPropertyNames() + } else { + gm.exportedNames = make([]string, 0, len(named)) + for name := range named { + gm.exportedNames = append(gm.exportedNames, name) + } } + for _, callback := range gm.exportedNamesCallbacks { callback(gm.exportedNames) }