Skip to content
File

Blob: src/workerd/api/commonjs.c++

5.4 KB
1#include "commonjs.h"
2 
3#include <workerd/io/features.h>
4#include <workerd/jsg/jsg.h>
5#include <workerd/jsg/modules-new.h>
6#include <workerd/jsg/resource.h>
7 
8namespace workerd::api {
9 
10CommonJsModuleContext::CommonJsModuleContext(jsg::Lock& js, kj::Path path)
11 : module(js.alloc<CommonJsModuleObject>(js, path.toString(true))),
12 pathOrSpecifier(kj::mv(path)),
13 exports(js, module->getExports(js)) {}
14 
15CommonJsModuleContext::CommonJsModuleContext(jsg::Lock& js, const jsg::Url& specifier)
16 : module(js.alloc<CommonJsModuleObject>(js, kj::str(specifier.getHref()))),
17 pathOrSpecifier(specifier.clone()),
18 exports(js, module->getExports(js)) {}
19 
20jsg::JsValue CommonJsModuleContext::require(jsg::Lock& js, kj::String specifier) {
21 if (isNodeJsCompatEnabled(js)) {
22 KJ_IF_SOME(nodeSpec, jsg::checkNodeSpecifier(specifier)) {
23 specifier = kj::mv(nodeSpec);
24 }
25 }
26 
27 if (FeatureFlags::get(js).getNewModuleRegistry()) {
28 auto& referrer = KJ_ASSERT_NONNULL(pathOrSpecifier.tryGet<jsg::Url>());
29 KJ_IF_SOME(ns,
30 jsg::modules::ModuleRegistry::tryResolveModuleNamespace(js, specifier,
31 jsg::modules::ResolveContext::Type::BUNDLE,
32 jsg::modules::ResolveContext::Source::REQUIRE, referrer,
33 jsg::modules::UnwrapDefault::YES)) {
34 return ns;
35 }
36 JSG_FAIL_REQUIRE(Error, kj::str("Module not found: ", specifier));
37 }
38 
39 auto& path = KJ_ASSERT_NONNULL(pathOrSpecifier.tryGet<kj::Path>());
40 
41 auto modulesForResolveCallback = jsg::getModulesForResolveCallback(js.v8Isolate);
42 KJ_REQUIRE(modulesForResolveCallback != nullptr, "didn't expect resolveCallback() now");
43 
44 kj::Path targetPath = ([&] {
45 // If the specifier begins with one of our known prefixes, let's not resolve
46 // it against the referrer.
47 if (specifier.startsWith("node:") || specifier.startsWith("cloudflare:") ||
48 specifier.startsWith("workerd:")) {
49 return kj::Path::parse(specifier);
50 }
51 return path.parent().eval(specifier);
52 })();
53 
54 // require() is only exposed to worker bundle modules so the resolve here is only
55 // permitted to require worker bundle or built-in modules. Internal modules are
56 // excluded.
57 auto& info =
58 JSG_REQUIRE_NONNULL(modulesForResolveCallback->resolve(js, targetPath, path,
59 jsg::ModuleRegistry::ResolveOption::DEFAULT,
60 jsg::ModuleRegistry::ResolveMethod::REQUIRE, specifier.asPtr()),
61 Error, "No such module \"", targetPath.toString(), "\".");
62 // Adding imported from suffix here not necessary like it is for resolveCallback, since we have a
63 // js stack that will include the parent module's name and location of the failed require().
64 
65 auto options = jsg::ModuleRegistry::RequireImplOptions::DEFAULT;
66 if (FeatureFlags::get(js).getExportCommonJsDefaultNamespace()) {
67 options = jsg::ModuleRegistry::RequireImplOptions::EXPORT_DEFAULT;
68 }
69 
70 return jsg::ModuleRegistry::requireImpl(js, info, options);
71}
72 
73void CommonJsModuleContext::visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
74 tracker.trackField("exports", exports);
75 KJ_SWITCH_ONEOF(pathOrSpecifier) {
76 KJ_CASE_ONEOF(path, kj::Path) {
77 tracker.trackFieldWithSize("path", path.size());
78 }
79 KJ_CASE_ONEOF(specifier, jsg::Url) {
80 tracker.trackField("specifier", specifier);
81 }
82 }
83}
84 
85kj::String CommonJsModuleContext::getFilename() const {
86 KJ_SWITCH_ONEOF(pathOrSpecifier) {
87 KJ_CASE_ONEOF(path, kj::Path) {
88 return path.toString(true);
89 }
90 KJ_CASE_ONEOF(specifier, jsg::Url) {
91 // The specifier is a URL. We want to parse it as a path and
92 // return just the filename portion.
93 // TODO(soon): kj::Path::parse() requires a kj::StringPtr but
94 // the path name here is a kj::ArrayPtr<const char>. We can
95 // avoid an extraneous copy here by updating kj::Path::parse
96 // to also accept a kj::ArrayPtr<const char>.
97 auto path = kj::str(specifier.getPathname().slice(1));
98 auto filename = kj::Path::parse(path).basename();
99 return filename.toString(false);
100 }
101 }
102 KJ_UNREACHABLE;
103}
104 
105kj::String CommonJsModuleContext::getDirname() const {
106 KJ_SWITCH_ONEOF(pathOrSpecifier) {
107 KJ_CASE_ONEOF(path, kj::Path) {
108 return path.parent().toString(true);
109 }
110 KJ_CASE_ONEOF(specifier, jsg::Url) {
111 // The specifier is a URL. We want to parse it as a path and
112 // return just the directory portion.
113 auto path = kj::str(specifier.getPathname().slice(1));
114 auto pathObj = kj::Path::parse(path);
115 return pathObj.parent().toString(true);
116 }
117 }
118 KJ_UNREACHABLE;
119}
120 
121jsg::Ref<CommonJsModuleObject> CommonJsModuleContext::getModule(jsg::Lock& js) {
122 return module.addRef();
123}
124 
125jsg::JsValue CommonJsModuleContext::getExports(jsg::Lock& js) const {
126 return exports.getHandle(js);
127}
128void CommonJsModuleContext::setExports(jsg::Lock& js, jsg::JsValue value) {
129 exports = jsg::JsRef(js, value);
130}
131 
132CommonJsModuleObject::CommonJsModuleObject(jsg::Lock& js, kj::String path)
133 : exports(js, js.obj()),
134 path(kj::mv(path)) {}
135 
136jsg::JsValue CommonJsModuleObject::getExports(jsg::Lock& js) const {
137 return exports.getHandle(js);
138}
139void CommonJsModuleObject::setExports(jsg::Lock& js, jsg::JsValue value) {
140 exports = jsg::JsRef(js, value);
141}
142 
143kj::StringPtr CommonJsModuleObject::getPath() const {
144 return path;
145}
146 
147void CommonJsModuleObject::visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
148 tracker.trackField("exports", exports);
149 tracker.trackField("path", path);
150}
151} // namespace workerd::api