From 0160772d25105f302f84013e892fe07f9eeb6f91 Mon Sep 17 00:00:00 2001 From: dave caruso Date: Mon, 2 Oct 2023 15:24:38 -0700 Subject: a --- docs/runtime/nodejs-apis.md | 4 +- src/bun.js/bindings/CommonJSModuleRecord.cpp | 14 ++-- src/bun.js/modules/NodeModuleModule.h | 96 ++++++++++++++++++---------- src/js/builtins.d.ts | 2 +- src/js/builtins/Module.ts | 15 ++++- src/js/out/WebCoreJSBuiltins.cpp | 12 +++- src/js/out/WebCoreJSBuiltins.h | 11 ++++ src/js/private.d.ts | 2 +- 8 files changed, 108 insertions(+), 48 deletions(-) diff --git a/docs/runtime/nodejs-apis.md b/docs/runtime/nodejs-apis.md index 5c6b858da..dbd32bd43 100644 --- a/docs/runtime/nodejs-apis.md +++ b/docs/runtime/nodejs-apis.md @@ -50,7 +50,7 @@ This page is updated regularly to reflect compatibility status of the latest ver ### [`node:events`](https://nodejs.org/api/events.html) -🟡 Missing `require('node:events').on`. +🟡 Missing `events.on`. ### [`node:fs`](https://nodejs.org/api/fs.html) @@ -74,7 +74,7 @@ This page is updated regularly to reflect compatibility status of the latest ver ### [`node:module`](https://nodejs.org/api/module.html) -🟢 Fully implemented. +🟡 Missing `module.register`, `module.syncBuiltinESMExports`, `module.findSourceMap`, `module.SourceMap`. ### [`node:net`](https://nodejs.org/api/net.html) diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index 38b55ba4d..103f62237 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -810,15 +810,17 @@ const JSC::ClassInfo JSCommonJSModule::s_info = { "Module"_s, &Base::s_info, nul const JSC::ClassInfo RequireResolveFunctionPrototype::s_info = { "resolve"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(RequireResolveFunctionPrototype) }; const JSC::ClassInfo RequireFunctionPrototype::s_info = { "require"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(RequireFunctionPrototype) }; +// This is .$require on a CommonJSModuleRecord. It is used by the CJS module loader internals in `Module.ts` JSC_DEFINE_HOST_FUNCTION(jsFunctionRequireCommonJS, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe)) { auto* globalObject = jsCast(lexicalGlobalObject); auto& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); + ASSERT(callframe->argumentCount() == 2); + JSCommonJSModule* thisObject = jsDynamicCast(callframe->thisValue()); - if (!thisObject) - return throwVMTypeError(globalObject, throwScope); + RELEASE_ASSERT(thisObject); JSValue specifierValue = callframe->argument(0); WTF::String specifier = specifierValue.toWTFString(globalObject); @@ -826,19 +828,19 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionRequireCommonJS, (JSGlobalObject * lexicalGlo // Special-case for "process" to just return the process object directly. if (UNLIKELY(specifier == "process"_s || specifier == "node:process"_s)) { - jsCast(callframe->argument(1))->putDirect(vm, builtinNames(vm).exportsPublicName(), globalObject->processObject(), 0); + thisObject->putDirect(vm, builtinNames(vm).exportsPublicName(), globalObject->processObject(), 0); return JSValue::encode(globalObject->processObject()); } - WTF::String referrer = thisObject->id().toWTFString(globalObject); - RETURN_IF_EXCEPTION(throwScope, {}); + JSValue referrerModule = callframe->argument(1); + WTF::String referrer = referrerModule.isString() ? referrerModule.toWTFString(globalObject) : MAKE_STATIC_STRING_IMPL("."); BunString specifierStr = Bun::toString(specifier); BunString referrerStr = Bun::toString(referrer); JSValue fetchResult = Bun::fetchCommonJSModule( globalObject, - jsCast(callframe->argument(1)), + thisObject, specifierValue, &specifierStr, &referrerStr); diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index eeac4c0ea..70eacad47 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -1,3 +1,4 @@ +// clang-format off #pragma once #include "CommonJSModuleRecord.h" @@ -152,6 +153,38 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBuiltinModule, return JSValue::encode(jsBoolean(Bun::isBuiltinModule(moduleStr))); } +JSC_DEFINE_HOST_FUNCTION(jsFunctionDebugNoop, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionFindPath, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + JSC::VM &vm = globalObject->vm(); + + auto specifier = callFrame->argument(0); + auto paths = jsDynamicCast(callFrame->argument(1)); + + if (!specifier.isString()) { + return JSValue::encode(jsBoolean(false)); + } + if (!paths) { + return JSValue::encode(jsBoolean(false)); + } + + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from, isESM); + + if (JSC::JSValue::decode(result).isString()) { + return result; + } + + // TODO: iterate + + return JSValue::encode(jsBoolean(false)); +} + // Might be faster as a JS builtin JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModulePreloadModules, (JSC::JSGlobalObject * globalObject, @@ -230,8 +263,10 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionFindSourceMap, CallFrame *callFrame)) { auto &vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - throwException(globalObject, scope, - createError(globalObject, "Not implemented"_s)); + throwException( + globalObject, scope, + createError(globalObject, + "module.findSourceMap is not implemented in Bun"_s)); return JSValue::encode(jsUndefined()); } @@ -241,7 +276,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionRegister, (JSGlobalObject * globalObject, auto scope = DECLARE_THROW_SCOPE(vm); throwException( globalObject, scope, - createError(globalObject, "Bun does not support ESM loaders"_s)); + createError(globalObject, "Bun does not support Node.js loaders"_s)); return JSValue::encode(jsUndefined()); } @@ -255,8 +290,10 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionSourceMap, (JSGlobalObject * globalObject, CallFrame *callFrame)) { auto &vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - throwException(globalObject, scope, - createError(globalObject, "Not implemented"_s)); + throwException( + globalObject, scope, + createError(globalObject, + "module.SourceMap is not yet implemented in Bun"_s)); return JSValue::encode(jsUndefined()); } @@ -406,43 +443,36 @@ DEFINE_NATIVE_MODULE(NodeModule) { put(Identifier::fromString(vm, "Module"_s), defaultObject); // Module._extensions === require.extensions - put(Identifier::fromString(vm, "_extensions"_s), - globalObject->requireFunctionUnbound()->get( - globalObject, Identifier::fromString(vm, "extensions"_s))); + put( + Identifier::fromString(vm, "_extensions"_s), + globalObject->requireFunctionUnbound()->get(globalObject, Identifier::fromString(vm, "extensions"_s)) + ); - put(Identifier::fromString(vm, "_pathCache"_s), - JSC::constructEmptyObject(globalObject)); + put(Identifier::fromString(vm, "_pathCache"_s), JSC::constructEmptyObject(globalObject)); + putNativeFn(Identifier::fromString(vm, "__resolveFilename"_s), jsFunctionResolveFileName); defaultObject->putDirectCustomAccessor( vm, JSC::Identifier::fromString(vm, "_resolveFilename"_s), - JSC::CustomGetterSetter::create(vm, get_resolveFilename, - set_resolveFilename), + JSC::CustomGetterSetter::create(vm, get_resolveFilename, set_resolveFilename), JSC::PropertyAttribute::CustomAccessor | 0); - putNativeFn(Identifier::fromString(vm, "__resolveFilename"_s), - jsFunctionResolveFileName); - - putNativeFn(Identifier::fromString(vm, "_preloadModules"_s), - jsFunctionNodeModulePreloadModules); - putNativeFn(Identifier::fromString(vm, "createRequire"_s), - jsFunctionNodeModuleCreateRequire); - putNativeFn(Identifier::fromString(vm, "paths"_s), - Resolver__nodeModulePathsForJS); - putNativeFn(Identifier::fromString(vm, "findSourceMap"_s), - jsFunctionFindSourceMap); - putNativeFn(Identifier::fromString(vm, "syncBuiltinExports"_s), - jsFunctionSyncBuiltinExports); + + putNativeFn(Identifier::fromString(vm, "_preloadModules"_s), jsFunctionNodeModulePreloadModules); + putNativeFn(Identifier::fromString(vm, "createRequire"_s), jsFunctionNodeModuleCreateRequire); + putNativeFn(Identifier::fromString(vm, "paths"_s), Resolver__nodeModulePathsForJS); + putNativeFn(Identifier::fromString(vm, "findSourceMap"_s), jsFunctionFindSourceMap); + putNativeFn(Identifier::fromString(vm, "syncBuiltinExports"_s), jsFunctionSyncBuiltinExports); putNativeFn(Identifier::fromString(vm, "SourceMap"_s), jsFunctionSourceMap); - putNativeFn(Identifier::fromString(vm, "isBuiltin"_s), - jsFunctionIsBuiltinModule); - putNativeFn(Identifier::fromString(vm, "_nodeModulePaths"_s), - Resolver__nodeModulePathsForJS); + putNativeFn(Identifier::fromString(vm, "isBuiltin"_s), jsFunctionIsBuiltinModule); + putNativeFn(Identifier::fromString(vm, "_nodeModulePaths"_s), Resolver__nodeModulePathsForJS); putNativeFn(Identifier::fromString(vm, "wrap"_s), jsFunctionWrap); - put(Identifier::fromString(vm, "_cache"_s), - jsCast(globalObject)->lazyRequireCacheObject()); + putNativeFn(Identifier::fromString(vm, "_debug"_s), jsFunctionDebugNoop); + + put(Identifier::fromString(vm, "_load"_s), JSFunction::create(vm, moduleModuleLoadCodeGenerator(vm), globalObject)); + + put(Identifier::fromString(vm, "_cache"_s), jsCast(globalObject)->lazyRequireCacheObject()); - put(Identifier::fromString(vm, "globalPaths"_s), - constructEmptyArray(globalObject, nullptr, 0)); + put(Identifier::fromString(vm, "globalPaths"_s), constructEmptyArray(globalObject, nullptr, 0)); auto prototype = constructEmptyObject(globalObject, globalObject->objectPrototype(), 1); diff --git a/src/js/builtins.d.ts b/src/js/builtins.d.ts index ee7bd68cf..99bf4d98e 100644 --- a/src/js/builtins.d.ts +++ b/src/js/builtins.d.ts @@ -438,7 +438,7 @@ declare function $createCommonJSModule( id: string, exports: any, hasEvaluated: boolean, - parent: CommonJSModuleRecord, + parent: CommonJSModuleRecord | undefined, ): CommonJSModuleRecord; declare function $overridableRequire(this: CommonJSModuleRecord, id: string): any; diff --git a/src/js/builtins/Module.ts b/src/js/builtins/Module.ts index b074d3488..3c9c2f1a0 100644 --- a/src/js/builtins/Module.ts +++ b/src/js/builtins/Module.ts @@ -9,7 +9,7 @@ export function require(this: CommonJSModuleRecord, id: string) { // overridableRequire can be overridden by setting `Module.prototype.require` export function overridableRequire(this: CommonJSModuleRecord, id: string) { - const existing = $requireMap.$get(id) || $requireMap.$get((id = $resolveSync(id, this.path, false))); + const existing = $requireMap.$get(id) || $requireMap.$get((id = $resolveSync(id, this?.path ?? ".", false))); if (existing) { // Scenario where this is necessary: // @@ -46,8 +46,7 @@ export function overridableRequire(this: CommonJSModuleRecord, id: string) { // // Note: we do not need to wrap this in a try/catch, if it throws the C++ code will // clear the module from the map. - // - var out = this.$require(id, mod); + var out = mod.$require(id, this?.id); // -1 means we need to lookup the module from the ESM registry. if (out === -1) { @@ -86,3 +85,13 @@ export function requireNativeModule(id: string) { } return $requireESM(id).default; } + +/** require('node:module')._load */ +export function moduleLoad(request: string, parentPath: CommonJSModuleRecord, isMain: string) { + // TODO: `isMain` does four things in node + // - sets `process.mainModule` + // - sets `module.require.main` + // - sets `module.id` to "." + // - would pass true to the third argument of _resolveFilename if overridden + return $overridableRequire.$call(parentPath, request); +} diff --git a/src/js/out/WebCoreJSBuiltins.cpp b/src/js/out/WebCoreJSBuiltins.cpp index 31246276b..1cea1826b 100644 --- a/src/js/out/WebCoreJSBuiltins.cpp +++ b/src/js/out/WebCoreJSBuiltins.cpp @@ -786,13 +786,21 @@ const int s_moduleMainCodeLength = 68; static const JSC::Intrinsic s_moduleMainCodeIntrinsic = JSC::NoIntrinsic; const char* const s_moduleMainCode = "(function () {\"use strict\";\n return @requireMap.@get(Bun.main);\n})\n"; +// moduleLoad +const JSC::ConstructAbility s_moduleModuleLoadCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_moduleModuleLoadCodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_moduleModuleLoadCodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_moduleModuleLoadCodeLength = 115; +static const JSC::Intrinsic s_moduleModuleLoadCodeIntrinsic = JSC::NoIntrinsic; +const char* const s_moduleModuleLoadCode = "(function (request, parentPath, isMain) {\"use strict\";\n return @overridableRequire.@call(parentPath, request);\n})\n"; + // overridableRequire const JSC::ConstructAbility s_moduleOverridableRequireCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_moduleOverridableRequireCodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_moduleOverridableRequireCodeImplementationVisibility = JSC::ImplementationVisibility::Public; -const int s_moduleOverridableRequireCodeLength = 888; +const int s_moduleOverridableRequireCodeLength = 900; static const JSC::Intrinsic s_moduleOverridableRequireCodeIntrinsic = JSC::NoIntrinsic; -const char* const s_moduleOverridableRequireCode = "(function (id) {\"use strict\";\n const existing = @requireMap.@get(id) || @requireMap.@get(id = @resolveSync(id, this.path, !1));\n if (existing)\n return @evaluateCommonJSModule(existing), existing.exports;\n if (id.endsWith(\".node\"))\n return @internalRequire(id);\n const mod = @createCommonJSModule(id, {}, !1, this);\n @requireMap.@set(id, mod);\n var out = this.@require(id, mod);\n if (out === -1) {\n try {\n out = @requireESM(id);\n } catch (exception) {\n throw @requireMap.@delete(id), exception;\n }\n const esm = @Loader.registry.@get(id);\n if (esm\?.evaluated && (esm.state \?\? 0) >= @ModuleReady) {\n const namespace = @Loader.getModuleNamespaceObject(esm.module);\n return mod.exports = namespace.__esModule \? namespace : Object.create(namespace, { __esModule: { value: !0 } });\n }\n }\n return @evaluateCommonJSModule(mod), mod.exports;\n})\n"; +const char* const s_moduleOverridableRequireCode = "(function (id) {\"use strict\";\n const existing = @requireMap.@get(id) || @requireMap.@get(id = @resolveSync(id, this\?.path \?\? \".\", !1));\n if (existing)\n return @evaluateCommonJSModule(existing), existing.exports;\n if (id.endsWith(\".node\"))\n return @internalRequire(id);\n const mod = @createCommonJSModule(id, {}, !1, this);\n @requireMap.@set(id, mod);\n var out = mod.@require(id, this\?.id);\n if (out === -1) {\n try {\n out = @requireESM(id);\n } catch (exception) {\n throw @requireMap.@delete(id), exception;\n }\n const esm = @Loader.registry.@get(id);\n if (esm\?.evaluated && (esm.state \?\? 0) >= @ModuleReady) {\n const namespace = @Loader.getModuleNamespaceObject(esm.module);\n return mod.exports = namespace.__esModule \? namespace : Object.create(namespace, { __esModule: { value: !0 } });\n }\n }\n return @evaluateCommonJSModule(mod), mod.exports;\n})\n"; // require const JSC::ConstructAbility s_moduleRequireCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; diff --git a/src/js/out/WebCoreJSBuiltins.h b/src/js/out/WebCoreJSBuiltins.h index 3c6ade197..5b8c0b267 100644 --- a/src/js/out/WebCoreJSBuiltins.h +++ b/src/js/out/WebCoreJSBuiltins.h @@ -1506,6 +1506,14 @@ extern const JSC::ConstructAbility s_moduleMainCodeConstructAbility; extern const JSC::ConstructorKind s_moduleMainCodeConstructorKind; extern const JSC::ImplementationVisibility s_moduleMainCodeImplementationVisibility; +// moduleLoad +#define WEBCORE_BUILTIN_MODULE_MODULELOAD 1 +extern const char* const s_moduleModuleLoadCode; +extern const int s_moduleModuleLoadCodeLength; +extern const JSC::ConstructAbility s_moduleModuleLoadCodeConstructAbility; +extern const JSC::ConstructorKind s_moduleModuleLoadCodeConstructorKind; +extern const JSC::ImplementationVisibility s_moduleModuleLoadCodeImplementationVisibility; + // overridableRequire #define WEBCORE_BUILTIN_MODULE_OVERRIDABLEREQUIRE 1 extern const char* const s_moduleOverridableRequireCode; @@ -1540,6 +1548,7 @@ extern const JSC::ImplementationVisibility s_moduleRequireResolveCodeImplementat #define WEBCORE_FOREACH_MODULE_BUILTIN_DATA(macro) \ macro(main, moduleMain, 0) \ + macro(moduleLoad, moduleModuleLoad, 3) \ macro(overridableRequire, moduleOverridableRequire, 1) \ macro(require, moduleRequire, 1) \ macro(requireNativeModule, moduleRequireNativeModule, 1) \ @@ -1547,6 +1556,7 @@ extern const JSC::ImplementationVisibility s_moduleRequireResolveCodeImplementat #define WEBCORE_FOREACH_MODULE_BUILTIN_CODE(macro) \ macro(moduleMainCode, main, "get main"_s, s_moduleMainCodeLength) \ + macro(moduleModuleLoadCode, moduleLoad, ASCIILiteral(), s_moduleModuleLoadCodeLength) \ macro(moduleOverridableRequireCode, overridableRequire, ASCIILiteral(), s_moduleOverridableRequireCodeLength) \ macro(moduleRequireCode, require, ASCIILiteral(), s_moduleRequireCodeLength) \ macro(moduleRequireNativeModuleCode, requireNativeModule, ASCIILiteral(), s_moduleRequireNativeModuleCodeLength) \ @@ -1554,6 +1564,7 @@ extern const JSC::ImplementationVisibility s_moduleRequireResolveCodeImplementat #define WEBCORE_FOREACH_MODULE_BUILTIN_FUNCTION_NAME(macro) \ macro(main) \ + macro(moduleLoad) \ macro(overridableRequire) \ macro(require) \ macro(requireNativeModule) \ diff --git a/src/js/private.d.ts b/src/js/private.d.ts index 77f4d5536..2ca8d630f 100644 --- a/src/js/private.d.ts +++ b/src/js/private.d.ts @@ -215,7 +215,7 @@ interface BunLazyModules { declare var $exports: any; interface CommonJSModuleRecord { - $require(id: string, mod: any): any; + $require(this: CommonJSModuleRecord, id: string, parent: string | undefined): any; children: CommonJSModuleRecord[]; exports: any; id: string; -- cgit v1.2.3