Skip to content
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

Fix namespaced object export when default is the only one available #4058

Merged
merged 11 commits into from
Nov 15, 2024
79 changes: 59 additions & 20 deletions js/module_loading_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
16 changes: 13 additions & 3 deletions js/modules/gomodule.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down