Skip to content

Commit

Permalink
Revert "Continue work on new module registry (#3372)" (#3401)
Browse files Browse the repository at this point in the history
This reverts commit da66065.
  • Loading branch information
mikea authored Jan 23, 2025
1 parent 83770a8 commit 148f755
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 244 deletions.
28 changes: 3 additions & 25 deletions src/workerd/api/tests/new-module-registry-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,6 @@ strictEqual(Buffer.from(data.default).toString(), 'abcdef');
import * as json from 'json';
deepStrictEqual(json.default, { foo: 1 });

import * as cjs from 'cjs';
deepStrictEqual(cjs.default, 1);

import * as nodejs from 'nodejs';
deepStrictEqual(nodejs.default, 2);

import { a as cjsA } from 'cjs2';
deepStrictEqual(cjsA, 1);

import * as cjs3 from 'cjs3';
deepStrictEqual(cjs.default, 1);

await rejects(import('invalid-json'), {
message: /Unexpected non-whitespace character after JSON/,
});
Expand Down Expand Up @@ -153,20 +141,10 @@ export const evalErrorsInEsmTopLevel = {
},
};

export const cjsThrow = {
async test() {
await rejects(import('cjs-throw'), {
message: 'boom',
});
},
};

// TODO(now): Tests
// * [ ] Include tests for all known module types
// * [x] ESM
// * [x] CommonJS
// * [x] CommonJS with top level errors
// * [ ] CommonJS with disallowed top-level i/o
// * [ ] CommonJS
// * [x] Text
// * [x] Data
// * [x] JSON
Expand All @@ -186,12 +164,12 @@ export const cjsThrow = {
// * [x] URL resolution works correctly
// * [x] Invalid URLs are correctly reported as errors
// * [x] Import assertions should be rejected
// * [x] require(...) Works in CommonJs Modules
// * [ ] require(...) Works in CommonJs Modules
// * [ ] require(...) correctly handles node: modules with/without the node: prefix
// * [ ] Circular dependencies are correctly handled
// * [ ] Errors during CommonJs evaluation are correctly reported
// * [ ] Entry point ESM with no default export is correctly reported as error
// * [x] CommonJs modules correctly expose named exports
// * [ ] CommonJs modules correctly expose named exports
// * [ ] require('module').createRequire API works as expected
// * [ ] Fallback service works as expected
// * [ ] console.log output correctly uses node-internal:inspect for output
Expand Down
9 changes: 0 additions & 9 deletions src/workerd/api/tests/new-module-registry-test.wd-test
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,6 @@ const unitTests :Workerd.Config = (
(name = "data", data = "abcdef"),
(name = "json", json = "{ \"foo\": 1 }"),
(name = "invalid-json", json = "1n"),
(name = "cjs", commonJsModule = "module.exports = 1"),
(name = "cjs-throw", commonJsModule = "throw new Error('boom')"),
(name = "nodejs", nodeJsCompatModule = "module.exports = 2"),
(name = "cjs2",
commonJsModule = "module.exports = { a: 1 }",
namedExports = ["a"],
),
(name = "cjs3",
commonJsModule = "module.exports = require('.././.././cjs')")
],
compatibilityDate = "2024-07-01",
compatibilityFlags = [
Expand Down
248 changes: 81 additions & 167 deletions src/workerd/jsg/commonjs.c++
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "commonjs.h"

#include "modules-new.h"
#include "modules.h"

namespace workerd::jsg {
Expand All @@ -10,95 +9,55 @@ CommonJsModuleContext::CommonJsModuleContext(jsg::Lock& js, kj::Path path)
path(kj::mv(path)),
exports(js.v8Isolate, module->getExports(js)) {}

CommonJsModuleContext::CommonJsModuleContext(jsg::Lock& js, const jsg::Url& url)
: module(jsg::alloc<CommonJsModuleObject>(js, kj::str(url.getHref()))),
path(url.clone()),
exports(js.v8Isolate, module->getExports(js)) {}

v8::Local<v8::Value> CommonJsModuleContext::require(jsg::Lock& js, kj::String specifier) {
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
auto modulesForResolveCallback = getModulesForResolveCallback(js.v8Isolate);
KJ_REQUIRE(modulesForResolveCallback != nullptr, "didn't expect resolveCallback() now");

if (isNodeJsCompatEnabled(js)) {
KJ_IF_SOME(nodeSpec, checkNodeSpecifier(specifier)) {
specifier = kj::mv(nodeSpec);
}
}

kj::Path targetPath = ([&] {
// If the specifier begins with one of our known prefixes, let's not resolve
// it against the referrer.
if (specifier.startsWith("node:") || specifier.startsWith("cloudflare:") ||
specifier.startsWith("workerd:")) {
return kj::Path::parse(specifier);
}
return p.parent().eval(specifier);
})();

// require() is only exposed to worker bundle modules so the resolve here is only
// permitted to require worker bundle or built-in modules. Internal modules are
// excluded.
auto& info =
JSG_REQUIRE_NONNULL(modulesForResolveCallback->resolve(js, targetPath, p,
ModuleRegistry::ResolveOption::DEFAULT,
ModuleRegistry::ResolveMethod::REQUIRE, specifier.asPtr()),
Error, "No such module \"", targetPath.toString(), "\".");
// Adding imported from suffix here not necessary like it is for
// resolveCallback, since we have a js stack that will include the parent
// module's name and location of the failed require().

ModuleRegistry::RequireImplOptions options = ModuleRegistry::RequireImplOptions::DEFAULT;
if (getCommonJsExportDefault(js.v8Isolate)) {
options = ModuleRegistry::RequireImplOptions::EXPORT_DEFAULT;
}

return ModuleRegistry::requireImpl(js, info, options);
}
KJ_CASE_ONEOF(u, jsg::Url) {
return modules::ModuleRegistry::resolve(js, specifier, "default"_kj,
modules::ResolveContext::Type::BUNDLE, modules::ResolveContext::Source::REQUIRE, u);
auto modulesForResolveCallback = getModulesForResolveCallback(js.v8Isolate);
KJ_REQUIRE(modulesForResolveCallback != nullptr, "didn't expect resolveCallback() now");

if (isNodeJsCompatEnabled(js)) {
KJ_IF_SOME(nodeSpec, checkNodeSpecifier(specifier)) {
specifier = kj::mv(nodeSpec);
}
}
KJ_UNREACHABLE;

kj::Path targetPath = ([&] {
// If the specifier begins with one of our known prefixes, let's not resolve
// it against the referrer.
if (specifier.startsWith("node:") || specifier.startsWith("cloudflare:") ||
specifier.startsWith("workerd:")) {
return kj::Path::parse(specifier);
}
return path.parent().eval(specifier);
})();

// require() is only exposed to worker bundle modules so the resolve here is only
// permitted to require worker bundle or built-in modules. Internal modules are
// excluded.
auto& info = JSG_REQUIRE_NONNULL(modulesForResolveCallback->resolve(js, targetPath, path,
ModuleRegistry::ResolveOption::DEFAULT,
ModuleRegistry::ResolveMethod::REQUIRE, specifier.asPtr()),
Error, "No such module \"", targetPath.toString(), "\".");
// Adding imported from suffix here not necessary like it is for resolveCallback, since we have a
// js stack that will include the parent module's name and location of the failed require().

ModuleRegistry::RequireImplOptions options = ModuleRegistry::RequireImplOptions::DEFAULT;
if (getCommonJsExportDefault(js.v8Isolate)) {
options = ModuleRegistry::RequireImplOptions::EXPORT_DEFAULT;
}

return ModuleRegistry::requireImpl(js, info, options);
}

void CommonJsModuleContext::visitForMemoryInfo(MemoryTracker& tracker) const {
tracker.trackField("exports", exports);
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
tracker.trackFieldWithSize("path", p.size());
}
KJ_CASE_ONEOF(u, jsg::Url) {
tracker.trackFieldWithSize("path", u.getHref().size());
}
}
tracker.trackFieldWithSize("path", path.size());
}

kj::String CommonJsModuleContext::getFilename() const {
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
return p.toString(true);
}
KJ_CASE_ONEOF(u, jsg::Url) {
return kj::str(u.getPathname());
}
}
KJ_UNREACHABLE;
return path.toString(true);
}

kj::String CommonJsModuleContext::getDirname() const {
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
return p.parent().toString(true);
}
// TODO(new-module-registry): Appropriately convert file URL into path
KJ_CASE_ONEOF(u, jsg::Url) {
return kj::str(u.getPathname());
}
}
KJ_UNREACHABLE;
return path.parent().toString(true);
}

jsg::Ref<CommonJsModuleObject> CommonJsModuleContext::getModule(jsg::Lock& js) {
Expand All @@ -108,7 +67,7 @@ jsg::Ref<CommonJsModuleObject> CommonJsModuleContext::getModule(jsg::Lock& js) {
v8::Local<v8::Value> CommonJsModuleContext::getExports(jsg::Lock& js) const {
return exports.getHandle(js);
}
void CommonJsModuleContext::setExports(jsg::Lock& js, jsg::Value value) {
void CommonJsModuleContext::setExports(jsg::Value value) {
exports = kj::mv(value);
}

Expand Down Expand Up @@ -139,66 +98,50 @@ NodeJsModuleContext::NodeJsModuleContext(jsg::Lock& js, kj::Path path)
path(kj::mv(path)),
exports(js.v8Ref(module->getExports(js))) {}

NodeJsModuleContext::NodeJsModuleContext(jsg::Lock& js, const jsg::Url& url)
: module(jsg::alloc<NodeJsModuleObject>(js, kj::str(url.getHref()))),
path(url.clone()),
exports(js.v8Isolate, module->getExports(js)) {}

v8::Local<v8::Value> NodeJsModuleContext::require(jsg::Lock& js, kj::String specifier) {
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
// If it is a bare specifier known to be a Node.js built-in, then prefix the
// specifier with node:
bool isNodeBuiltin = false;
auto resolveOption = jsg::ModuleRegistry::ResolveOption::DEFAULT;
KJ_IF_SOME(spec, checkNodeSpecifier(specifier)) {
specifier = kj::mv(spec);
isNodeBuiltin = true;
resolveOption = jsg::ModuleRegistry::ResolveOption::BUILTIN_ONLY;
}

// TODO(cleanup): This implementation from here on is identical to the
// CommonJsModuleContext::require. We should consolidate these as the
// next step.

auto modulesForResolveCallback = jsg::getModulesForResolveCallback(js.v8Isolate);
KJ_REQUIRE(modulesForResolveCallback != nullptr, "didn't expect resolveCallback() now");

kj::Path targetPath = ([&] {
// If the specifier begins with one of our known prefixes, let's not resolve
// it against the referrer.
if (specifier.startsWith("node:") || specifier.startsWith("cloudflare:") ||
specifier.startsWith("workerd:")) {
return kj::Path::parse(specifier);
}
return p.parent().eval(specifier);
})();

// require() is only exposed to worker bundle modules so the resolve here is only
// permitted to require worker bundle or built-in modules. Internal modules are
// excluded.
auto& info =
JSG_REQUIRE_NONNULL(modulesForResolveCallback->resolve(js, targetPath, p, resolveOption,
ModuleRegistry::ResolveMethod::REQUIRE, specifier.asPtr()),
Error, "No such module \"", targetPath.toString(), "\".");
// Adding imported from suffix here not necessary like it is for resolveCallback,
// since we have a js stack that will include the parent module's name and location
// of the failed require().

if (!isNodeBuiltin) {
JSG_REQUIRE_NONNULL(
info.maybeSynthetic, TypeError, "Cannot use require() to import an ES Module.");
}

return ModuleRegistry::requireImpl(
js, info, ModuleRegistry::RequireImplOptions::EXPORT_DEFAULT);
}
KJ_CASE_ONEOF(u, jsg::Url) {
return modules::ModuleRegistry::resolve(js, specifier, "default"_kj,
modules::ResolveContext::Type::BUNDLE, modules::ResolveContext::Source::REQUIRE, u);
}
// If it is a bare specifier known to be a Node.js built-in, then prefix the
// specifier with node:
bool isNodeBuiltin = false;
auto resolveOption = jsg::ModuleRegistry::ResolveOption::DEFAULT;
KJ_IF_SOME(spec, checkNodeSpecifier(specifier)) {
specifier = kj::mv(spec);
isNodeBuiltin = true;
resolveOption = jsg::ModuleRegistry::ResolveOption::BUILTIN_ONLY;
}
KJ_UNREACHABLE;

// TODO(cleanup): This implementation from here on is identical to the
// CommonJsModuleContext::require. We should consolidate these as the
// next step.

auto modulesForResolveCallback = jsg::getModulesForResolveCallback(js.v8Isolate);
KJ_REQUIRE(modulesForResolveCallback != nullptr, "didn't expect resolveCallback() now");

kj::Path targetPath = ([&] {
// If the specifier begins with one of our known prefixes, let's not resolve
// it against the referrer.
if (specifier.startsWith("node:") || specifier.startsWith("cloudflare:") ||
specifier.startsWith("workerd:")) {
return kj::Path::parse(specifier);
}
return path.parent().eval(specifier);
})();

// require() is only exposed to worker bundle modules so the resolve here is only
// permitted to require worker bundle or built-in modules. Internal modules are
// excluded.
auto& info =
JSG_REQUIRE_NONNULL(modulesForResolveCallback->resolve(js, targetPath, path, resolveOption,
ModuleRegistry::ResolveMethod::REQUIRE, specifier.asPtr()),
Error, "No such module \"", targetPath.toString(), "\".");
// Adding imported from suffix here not necessary like it is for resolveCallback, since we have a
// js stack that will include the parent module's name and location of the failed require().

if (!isNodeBuiltin) {
JSG_REQUIRE_NONNULL(
info.maybeSynthetic, TypeError, "Cannot use require() to import an ES Module.");
}

return ModuleRegistry::requireImpl(js, info, ModuleRegistry::RequireImplOptions::EXPORT_DEFAULT);
}

v8::Local<v8::Value> NodeJsModuleContext::getBuffer(jsg::Lock& js) {
Expand All @@ -217,28 +160,11 @@ v8::Local<v8::Value> NodeJsModuleContext::getProcess(jsg::Lock& js) {
}

kj::String NodeJsModuleContext::getFilename() {
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
return p.toString(true);
}
KJ_CASE_ONEOF(u, jsg::Url) {
return kj::str(u.getPathname());
}
}
KJ_UNREACHABLE;
return path.toString(true);
}

kj::String NodeJsModuleContext::getDirname() {
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
return p.parent().toString(true);
}
// TODO(new-module-registry): Appropriately convert file URL into path
KJ_CASE_ONEOF(u, jsg::Url) {
return kj::str(u.getPathname());
}
}
KJ_UNREACHABLE;
return path.parent().toString(true);
}

jsg::Ref<NodeJsModuleObject> NodeJsModuleContext::getModule(jsg::Lock& js) {
Expand Down Expand Up @@ -269,16 +195,4 @@ kj::StringPtr NodeJsModuleObject::getPath() {
return path;
}

void NodeJsModuleContext::visitForMemoryInfo(MemoryTracker& tracker) const {
tracker.trackField("exports", exports);
KJ_SWITCH_ONEOF(path) {
KJ_CASE_ONEOF(p, kj::Path) {
tracker.trackFieldWithSize("path", p.size());
}
KJ_CASE_ONEOF(u, jsg::Url) {
tracker.trackFieldWithSize("path", u.getHref().size());
}
}
}

} // namespace workerd::jsg
Loading

0 comments on commit 148f755

Please sign in to comment.