Skip to content

Commit 0788764

Browse files
committed
Allow core modules to be loaded both with and without the 'node:' prefix.
1 parent 804a845 commit 0788764

File tree

9 files changed

+149
-17
lines changed

9 files changed

+149
-17
lines changed

.github/workflows/main.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@ jobs:
1111
runs-on: ${{ matrix.os }}
1212
steps:
1313
- name: Install Go
14-
uses: actions/setup-go@v3
14+
uses: actions/setup-go@v4
1515
with:
1616
go-version: ${{ matrix.go-version }}
1717
- name: Checkout code
1818
uses: actions/checkout@v3
1919
- name: Check formatting
2020
run: diff -u <(echo -n) <(gofmt -d .)
2121
if: runner.os != 'Windows'
22+
- name: Run go vet
23+
env:
24+
GOARCH: ${{ matrix.arch }}
25+
run: go vet ./...
2226
- name: Run tests
2327
env:
2428
GOARCH: ${{ matrix.arch }}
25-
run: go test ./...
29+
run: go test -vet=off ./...

buffer/buffer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"golang.org/x/text/encoding/unicode"
1515
)
1616

17-
const ModuleName = "node:buffer"
17+
const ModuleName = "buffer"
1818

1919
type Buffer struct {
2020
r *goja.Runtime
@@ -442,5 +442,5 @@ func Require(runtime *goja.Runtime, module *goja.Object) {
442442
}
443443

444444
func init() {
445-
require.RegisterNativeModule(ModuleName, Require)
445+
require.RegisterCoreModule(ModuleName, Require)
446446
}

console/module.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/dop251/goja_nodejs/util"
99
)
1010

11-
const ModuleName = "node:console"
11+
const ModuleName = "console"
1212

1313
type Console struct {
1414
runtime *goja.Runtime
@@ -78,5 +78,5 @@ func Enable(runtime *goja.Runtime) {
7878
}
7979

8080
func init() {
81-
require.RegisterNativeModule(ModuleName, Require)
81+
require.RegisterCoreModule(ModuleName, Require)
8282
}

process/module.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/dop251/goja_nodejs/require"
99
)
1010

11+
const ModuleName = "process"
12+
1113
type Process struct {
1214
env map[string]string
1315
}
@@ -27,9 +29,9 @@ func Require(runtime *goja.Runtime, module *goja.Object) {
2729
}
2830

2931
func Enable(runtime *goja.Runtime) {
30-
runtime.Set("process", require.Require(runtime, "process"))
32+
runtime.Set("process", require.Require(runtime, ModuleName))
3133
}
3234

3335
func init() {
34-
require.RegisterNativeModule("process", Require)
36+
require.RegisterCoreModule(ModuleName, Require)
3537
}

require/module.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ type ModuleLoader func(*js.Runtime, *js.Object)
2424
type SourceLoader func(path string) ([]byte, error)
2525

2626
var (
27-
InvalidModuleError = errors.New("Invalid module")
28-
IllegalModuleNameError = errors.New("Illegal module name")
29-
27+
InvalidModuleError = errors.New("Invalid module")
28+
IllegalModuleNameError = errors.New("Illegal module name")
29+
NoSuchBuiltInModuleError = errors.New("No such built-in module")
3030
ModuleFileDoesNotExistError = errors.New("module file does not exist")
3131
)
3232

33-
var native map[string]ModuleLoader
33+
var native, builtin map[string]ModuleLoader
3434

3535
// Registry contains a cache of compiled modules which can be used by multiple Runtimes
3636
type Registry struct {
@@ -217,10 +217,30 @@ func Require(runtime *js.Runtime, name string) js.Value {
217217
panic(runtime.NewTypeError("Please enable require for this runtime using new(require.Registry).Enable(runtime)"))
218218
}
219219

220+
// RegisterNativeModule registers a module that isn't loaded through a SourceLoader, but rather through
221+
// a provided ModuleLoader. Typically, this will be a module implemented in Go (although theoretically
222+
// it can be anything, depending on the ModuleLoader implementation).
223+
// Such modules take precedence over modules loaded through a SourceLoader, i.e. if a module name resolves as
224+
// native, the native module is loaded, and the SourceLoader is not consulted.
225+
// The binding is global and affects all instances of Registry.
226+
// It should be called from a package init() function as it may not be used concurrently with require() calls.
227+
// For registry-specific bindings see Registry.RegisterNativeModule.
220228
func RegisterNativeModule(name string, loader ModuleLoader) {
221229
if native == nil {
222230
native = make(map[string]ModuleLoader)
223231
}
224232
name = filepathClean(name)
225233
native[name] = loader
226234
}
235+
236+
// RegisterCoreModule registers a nodejs core module. If the name does not start with "node:", the module
237+
// will also be loadable as "node:<name>". Hence, for "builtin" modules (such as buffer, console, etc.)
238+
// the name should not include the "node:" prefix, but for prefix-only core modules (such as "node:test")
239+
// it should include the prefix.
240+
func RegisterCoreModule(name string, loader ModuleLoader) {
241+
if builtin == nil {
242+
builtin = make(map[string]ModuleLoader)
243+
}
244+
name = filepathClean(name)
245+
builtin[name] = loader
246+
}

require/module_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,84 @@ func TestRequireNativeModule(t *testing.T) {
5050
}
5151
}
5252

53+
func TestRegisterCoreModule(t *testing.T) {
54+
vm := js.New()
55+
56+
registry := new(Registry)
57+
registry.Enable(vm)
58+
59+
RegisterCoreModule("coremod", func(runtime *js.Runtime, module *js.Object) {
60+
o := module.Get("exports").(*js.Object)
61+
o.Set("test", func(call js.FunctionCall) js.Value {
62+
return runtime.ToValue("passed")
63+
})
64+
})
65+
66+
RegisterCoreModule("coremod1", func(runtime *js.Runtime, module *js.Object) {
67+
o := module.Get("exports").(*js.Object)
68+
o.Set("test", func(call js.FunctionCall) js.Value {
69+
return runtime.ToValue("passed1")
70+
})
71+
})
72+
73+
RegisterCoreModule("node:test1", func(runtime *js.Runtime, module *js.Object) {
74+
o := module.Get("exports").(*js.Object)
75+
o.Set("test", func(call js.FunctionCall) js.Value {
76+
return runtime.ToValue("test1 passed")
77+
})
78+
})
79+
80+
registry.RegisterNativeModule("bob", func(runtime *js.Runtime, module *js.Object) {
81+
82+
})
83+
84+
_, err := vm.RunString(`
85+
const m1 = require("coremod");
86+
const m2 = require("node:coremod");
87+
if (m1 !== m2) {
88+
throw new Error("Modules are not equal");
89+
}
90+
if (m1.test() !== "passed") {
91+
throw new Error("m1.test() has failed");
92+
}
93+
94+
const m3 = require("node:coremod1");
95+
const m4 = require("coremod1");
96+
if (m3 !== m4) {
97+
throw new Error("Modules are not equal (1)");
98+
}
99+
if (m3.test() !== "passed1") {
100+
throw new Error("m3.test() has failed");
101+
}
102+
103+
try {
104+
require("node:bob");
105+
} catch (e) {
106+
if (!e.message.includes("No such built-in module")) {
107+
throw e;
108+
}
109+
}
110+
require("bob");
111+
112+
try {
113+
require("test1");
114+
throw new Error("Expected exception");
115+
} catch (e) {
116+
if (!e.message.includes("Invalid module")) {
117+
throw e;
118+
}
119+
}
120+
121+
if (require("node:test1").test() !== "test1 passed") {
122+
throw new Error("test1.test() has failed");
123+
}
124+
`)
125+
126+
if err != nil {
127+
t.Fatal(err)
128+
}
129+
}
130+
53131
func TestRequireRegistryNativeModule(t *testing.T) {
54132
const SCRIPT = `
55133
var log = require("test/log");

require/resolve.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
js "github.com/dop251/goja"
1010
)
1111

12+
const NodePrefix = "node:"
13+
1214
// NodeJS module search algorithm described by
1315
// https://nodejs.org/api/modules.html#modules_all_together
1416
func (r *RequireModule) resolve(modpath string) (module *js.Object, err error) {
@@ -41,7 +43,11 @@ func (r *RequireModule) resolve(modpath string) (module *js.Object, err error) {
4143
if err == nil {
4244
return
4345
} else {
44-
err = nil
46+
if err == InvalidModuleError {
47+
err = nil
48+
} else {
49+
return
50+
}
4551
}
4652
if module = r.nodeModules[p]; module != nil {
4753
return
@@ -69,9 +75,31 @@ func (r *RequireModule) loadNative(path string) (*js.Object, error) {
6975
ldr = native[path]
7076
}
7177

78+
var isBuiltIn, withPrefix bool
79+
if ldr == nil {
80+
ldr = builtin[path]
81+
if ldr == nil && strings.HasPrefix(path, NodePrefix) {
82+
ldr = builtin[path[len(NodePrefix):]]
83+
if ldr == nil {
84+
return nil, NoSuchBuiltInModuleError
85+
}
86+
withPrefix = true
87+
}
88+
isBuiltIn = true
89+
}
90+
7291
if ldr != nil {
7392
module = r.createModuleObject()
7493
r.modules[path] = module
94+
if isBuiltIn {
95+
if withPrefix {
96+
r.modules[path[len(NodePrefix):]] = module
97+
} else {
98+
if !strings.HasPrefix(path, NodePrefix) {
99+
r.modules[NodePrefix+path] = module
100+
}
101+
}
102+
}
75103
ldr(r.runtime, module)
76104
return module, nil
77105
}

url/module.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/dop251/goja_nodejs/require"
1414
)
1515

16-
const ModuleName = "node:url"
16+
const ModuleName = "url"
1717

1818
var (
1919
reflectTypeURL = reflect.TypeOf((*url.URL)(nil))
@@ -360,5 +360,5 @@ func Enable(runtime *goja.Runtime) {
360360
}
361361

362362
func init() {
363-
require.RegisterNativeModule(ModuleName, Require)
363+
require.RegisterCoreModule(ModuleName, Require)
364364
}

util/module.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"github.com/dop251/goja_nodejs/require"
77
)
88

9-
const ModuleName = "node:util"
9+
const ModuleName = "util"
1010

1111
type Util struct {
1212
runtime *goja.Runtime
@@ -100,5 +100,5 @@ func New(runtime *goja.Runtime) *Util {
100100
}
101101

102102
func init() {
103-
require.RegisterNativeModule(ModuleName, Require)
103+
require.RegisterCoreModule(ModuleName, Require)
104104
}

0 commit comments

Comments
 (0)