/** * How this works * * CommonJS modules are transpiled by Bun's transpiler to the following: * * (function (exports, require, module) { ... code })(exports, require, module) * * Then, at runtime, we create a JSCommonJSModule object. * * On this special object, we override the setter for the "exports" property in * a non-observable way using a CustomGetterSetter. * * When the setter is called, we set the internal "exports" property to the * value passed in and we also update the requireMap with the new value. * * After the CommonJS module is executed, we: * - Store the exports value in the requireMap (again) * - Loop through the keys of the exports object and re-export as ES Module * named exports * * If an exception occurs, we remove the entry from the requireMap. * * How cyclical dependencies are handled: * * Before executing the CommonJS module, we set the exports object in the * requireMap to an empty object. When the CommonJS module is required again, we * return the exports object from the requireMap. The values should be in sync * while the module is being executed, unless module.exports is re-assigned to a * different value. In that case, it will have a stale value. */ #include "headers.h" #include "JavaScriptCore/CallData.h" #include "JavaScriptCore/Synchronousness.h" #include "JavaScriptCore/JSCast.h" #include #include "root.h" #include "JavaScriptCore/SourceCode.h" #include "headers-handwritten.h" #include "ZigGlobalObject.h" #include #include #include #include #include #include #include #include #include #include "BunClientData.h" #include #include "ImportMetaObject.h" #include #include #include #include #include #include #include #include "ModuleLoader.h" #include #include #include #include "ZigSourceProvider.h" #include #include "JSCommonJSModule.h" #include #include #include #include #include "PathInlines.h" #include "wtf/NakedPtr.h" #include "wtf/URL.h" #include "wtf/text/StringImpl.h" extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const BunString*); namespace Bun { using namespace JSC; JSC_DECLARE_HOST_FUNCTION(jsFunctionRequireCommonJS); JSC_DECLARE_HOST_FUNCTION(jsFunctionRequireNativeModule); static bool canPerformFastEnumeration(Structure* s) { if (s->typeInfo().overridesGetOwnPropertySlot()) return false; if (s->typeInfo().overridesAnyFormOfGetOwnPropertyNames()) return false; if (hasIndexedProperties(s->indexingType())) return false; if (s->hasAnyKindOfGetterSetterProperties()) return false; if (s->isUncacheableDictionary()) return false; if (s->hasUnderscoreProtoPropertyExcludingOriginalProto()) return false; return true; } extern "C" bool Bun__VM__specifierIsEvalEntryPoint(void*, EncodedJSValue); extern "C" void Bun__VM__setEntryPointEvalResultCJS(void*, EncodedJSValue); static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObject, JSCommonJSModule* moduleObject, JSString* dirname, JSValue filename) { auto scope = DECLARE_THROW_SCOPE(vm); SourceCode code = std::move(moduleObject->sourceCode); // If an exception occurred somewhere else, we might have cleared the source code. if (UNLIKELY(code.isNull())) { throwException(globalObject, scope, createError(globalObject, "Failed to evaluate module"_s)); return false; } JSFunction* resolveFunction = nullptr; JSFunction* requireFunction = nullptr; const auto initializeModuleObject = [&]() { resolveFunction = JSC::JSBoundFunction::create(vm, globalObject, globalObject->requireResolveFunctionUnbound(), moduleObject->filename(), ArgList(), 1, globalObject->commonStrings().resolveString(globalObject)); requireFunction = JSC::JSBoundFunction::create(vm, globalObject, globalObject->requireFunctionUnbound(), moduleObject, ArgList(), 1, globalObject->commonStrings().requireString(globalObject)); requireFunction->putDirect(vm, vm.propertyNames->resolve, resolveFunction, 0); moduleObject->putDirect(vm, WebCore::clientData(vm)->builtinNames().requirePublicName(), requireFunction, 0); moduleObject->hasEvaluated = true; }; if (UNLIKELY(Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(filename)))) { initializeModuleObject(); // Using same approach as node, `arguments` in the entry point isn't defined // https://github.com/nodejs/node/blob/592c6907bfe1922f36240e9df076be1864c3d1bd/lib/internal/process/execution.js#L92 globalObject->putDirect(vm, builtinNames(vm).exportsPublicName(), moduleObject->exportsObject(), 0); globalObject->putDirect(vm, builtinNames(vm).requirePublicName(), requireFunction, 0); globalObject->putDirect(vm, Identifier::fromString(vm, "module"_s), moduleObject, 0); globalObject->putDirect(vm, Identifier::fromString(vm, "__filename"_s), filename, 0); globalObject->putDirect(vm, Identifier::fromString(vm, "__dirname"_s), dirname, 0); scope.assertNoException(); WTF::NakedPtr returnedException; JSValue result = JSC::evaluate(globalObject, code, jsUndefined(), returnedException); if (UNLIKELY(returnedException)) { scope.throwException(globalObject, returnedException.get()); return false; } ASSERT(!scope.exception()); ASSERT(result); Bun__VM__setEntryPointEvalResultCJS(globalObject->bunVM(), JSValue::encode(result)); RELEASE_AND_RETURN(scope, true); } WTF::NakedPtr returnedException; JSValue fnValue = JSC::evaluate(globalObject, code, jsUndefined(), returnedException); if (UNLIKELY(returnedException)) { scope.throwException(globalObject, returnedException.get()); RELEASE_AND_RETURN(scope, false); } ASSERT(!scope.exception()); ASSERT(fnValue); JSObject* fn = fnValue.getObject(); if (UNLIKELY(!fn)) { scope.throwException(globalObject, createTypeError(globalObject, "Expected CommonJS module to have a function wrapper. If you weren't messing around with Bun's internals, this is a bug in Bun"_s)); RELEASE_AND_RETURN(scope, false); } JSC::CallData callData = JSC::getCallData(fn); if (UNLIKELY(callData.type == CallData::Type::None)) { scope.throwException(globalObject, createTypeError(globalObject, "Expected CommonJS module to have a function wrapper. If you weren't messing around with Bun's internals, this is a bug in Bun"_s)); RELEASE_AND_RETURN(scope, false); } initializeModuleObject(); RETURN_IF_EXCEPTION(scope, false); MarkedArgumentBuffer args; args.append(moduleObject->exportsObject()); // exports args.append(requireFunction); // require args.append(moduleObject); // module args.append(filename); // filename args.append(dirname); // dirname if (auto* jsFunction = jsDynamicCast(fn)) { if (jsFunction->jsExecutable()->parameterCount() > 5) { // it expects ImportMetaObject args.append(Zig::ImportMetaObject::create(globalObject, filename)); } } // Clear the source code as early as possible. code = {}; // Call the CommonJS module wrapper function. // // fn(exports, require, module, __filename, __dirname) { /* code */ }(exports, require, module, __filename, __dirname) // JSC::profiledCall(globalObject, ProfilingReason::API, fn, callData, moduleObject, args, returnedException); if (UNLIKELY(returnedException)) { scope.throwException(globalObject, returnedException.get()); return false; } ASSERT(!scope.exception()); RELEASE_AND_RETURN(scope, true); } bool JSCommonJSModule::load(JSC::VM& vm, Zig::GlobalObject* globalObject) { auto scope = DECLARE_THROW_SCOPE(vm); if (this->hasEvaluated || this->sourceCode.isNull()) { return true; } evaluateCommonJSModuleOnce( globalObject->vm(), jsCast(globalObject), this, this->m_dirname.get(), this->m_filename.get()); if (auto exception = scope.exception()) { scope.clearException(); // On error, remove the module from the require map/ // so that it can be re-evaluated on the next require. bool wasRemoved = globalObject->requireMap()->remove(globalObject, this->filename()); ASSERT(wasRemoved); scope.throwException(globalObject, exception); return false; } return true; } JSC_DEFINE_HOST_FUNCTION(jsFunctionEvaluateCommonJSModule, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe)) { auto& vm = JSC::getVM(lexicalGlobalObject); auto* globalObject = jsCast(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); // These casts are jsDynamicCast because require.cache pollution + invalid // this calls can put arbitrary values here instead of JSCommonJSModule* ASSERT(callframe->argumentCount() == 2); JSCommonJSModule* moduleObject = jsDynamicCast(callframe->uncheckedArgument(0)); JSCommonJSModule* referrer = jsDynamicCast(callframe->uncheckedArgument(1)); if (UNLIKELY(!moduleObject)) { RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); } JSValue returnValue = jsNull(); if (LIKELY(referrer)) { if (UNLIKELY(referrer->m_childrenValue)) { // It's too hard to append from native code: // referrer.children.indexOf(moduleObject) === -1 && referrer.children.push(moduleObject) returnValue = referrer->m_childrenValue.get(); } else { referrer->m_children.append(WriteBarrier()); referrer->m_children.last().set(vm, referrer, moduleObject); } } moduleObject->load(vm, globalObject); RETURN_IF_EXCEPTION(throwScope, {}); RELEASE_AND_RETURN(throwScope, JSValue::encode(returnValue)); } JSC_DEFINE_HOST_FUNCTION(requireResolvePathsFunction, (JSGlobalObject * globalObject, CallFrame* callframe)) { return JSValue::encode(JSC::constructEmptyArray(globalObject, nullptr, 0)); } JSC_DEFINE_CUSTOM_GETTER(jsRequireCacheGetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { Zig::GlobalObject* thisObject = jsCast(globalObject); return JSValue::encode(thisObject->lazyRequireCacheObject()); } JSC_DEFINE_CUSTOM_SETTER(jsRequireCacheSetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSObject* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->putDirect(globalObject->vm(), propertyName, JSValue::decode(value), 0); return true; } static const HashTableValue RequireResolveFunctionPrototypeValues[] = { { "paths"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, requireResolvePathsFunction, 1 } }, }; static const HashTableValue RequireFunctionPrototypeValues[] = { { "cache"_s, static_cast(JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsRequireCacheGetter, jsRequireCacheSetter } }, }; Structure* RequireFunctionPrototype::createStructure( JSC::VM& vm, JSC::JSGlobalObject* globalObject) { auto* structure = Structure::create(vm, globalObject, globalObject->functionPrototype(), TypeInfo(ObjectType, StructureFlags), info()); structure->setMayBePrototype(true); return structure; } Structure* RequireResolveFunctionPrototype::createStructure( JSC::VM& vm, JSC::JSGlobalObject* globalObject) { auto* structure = Structure::create(vm, globalObject, globalObject->functionPrototype(), TypeInfo(ObjectType, StructureFlags), info()); structure->setMayBePrototype(true); return structure; } RequireResolveFunctionPrototype* RequireResolveFunctionPrototype::create(JSC::JSGlobalObject* globalObject) { auto& vm = JSC::getVM(globalObject); auto* structure = RequireResolveFunctionPrototype::createStructure(vm, globalObject); RequireResolveFunctionPrototype* prototype = new (NotNull, JSC::allocateCell(vm)) RequireResolveFunctionPrototype(vm, structure); prototype->finishCreation(vm); return prototype; } RequireFunctionPrototype* RequireFunctionPrototype::create( JSC::JSGlobalObject* globalObject) { auto& vm = JSC::getVM(globalObject); auto* structure = RequireFunctionPrototype::createStructure(vm, globalObject); RequireFunctionPrototype* prototype = new (NotNull, JSC::allocateCell(vm)) RequireFunctionPrototype(vm, structure); prototype->finishCreation(vm); prototype->putDirect(vm, vm.propertyNames->resolve, jsCast(globalObject)->requireResolveFunctionUnbound(), 0); return prototype; } void RequireFunctionPrototype::finishCreation(JSC::VM& vm) { Base::finishCreation(vm); ASSERT(inherits(info())); auto* globalObject = this->globalObject(); reifyStaticProperties(vm, info(), RequireFunctionPrototypeValues, *this); JSC::JSFunction* requireDotMainFunction = JSFunction::create( vm, globalObject, commonJSMainCodeGenerator(vm), globalObject->globalScope()); this->putDirectAccessor( globalObject, JSC::Identifier::fromString(vm, "main"_s), JSC::GetterSetter::create(vm, globalObject, requireDotMainFunction, requireDotMainFunction), PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | 0); auto extensions = constructEmptyObject(globalObject); extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".js"_s), jsBoolean(true), 0); extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".json"_s), jsBoolean(true), 0); extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".node"_s), jsBoolean(true), 0); this->putDirect(vm, JSC::Identifier::fromString(vm, "extensions"_s), extensions, 0); } JSC_DEFINE_CUSTOM_GETTER(getterFilename, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } return JSValue::encode(thisObject->m_filename.get()); } JSC_DEFINE_CUSTOM_GETTER(getterId, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } return JSValue::encode(thisObject->m_id.get()); } JSC_DEFINE_CUSTOM_GETTER(getterPath, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } return JSValue::encode(thisObject->m_dirname.get()); } JSC_DEFINE_CUSTOM_GETTER(getterParent, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } if (thisObject->m_overriddenParent) { return JSValue::encode(thisObject->m_overriddenParent.get()); } if (thisObject->m_parent) { auto* parent = thisObject->m_parent.get(); return JSValue::encode(parent); } // initialize parent by checking if it is the main module. we do this lazily because most people // dont need `module.parent` and creating commonjs module records is done a ton. auto idValue = thisObject->m_id.get(); if (idValue) { auto id = idValue->view(globalObject); if (id == "."_s) { thisObject->m_overriddenParent.set(globalObject->vm(), thisObject, jsNull()); return JSValue::encode(jsNull()); } } return JSValue::encode(jsUndefined()); } JSC_DEFINE_CUSTOM_SETTER(setterPath, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->m_dirname.set(globalObject->vm(), thisObject, JSValue::decode(value).toString(globalObject)); return true; } extern "C" JSC::EncodedJSValue Resolver__propForRequireMainPaths(JSGlobalObject*); JSC_DEFINE_CUSTOM_GETTER(getterPaths, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } if (!thisObject->m_paths) { JSValue paths = JSValue::decode(Resolver__propForRequireMainPaths(globalObject)); thisObject->m_paths.set(globalObject->vm(), thisObject, paths); } return JSValue::encode(thisObject->m_paths.get()); } JSC_DEFINE_CUSTOM_SETTER(setterChildren, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->m_children.clear(); thisObject->m_childrenValue.set(globalObject->vm(), thisObject, JSValue::decode(value)); return true; } JSC_DEFINE_CUSTOM_GETTER(getterChildren, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* mod = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!mod)) { return JSValue::encode(jsUndefined()); } if (!mod->m_childrenValue) { auto throwScope = DECLARE_THROW_SCOPE(globalObject->vm()); MarkedArgumentBuffer children; children.ensureCapacity(mod->m_children.size()); // Deduplicate children while preserving insertion order. JSCommonJSModule* last = nullptr; int n = -1; for (WriteBarrier childBarrier : mod->m_children) { JSCommonJSModule* child = jsCast(childBarrier.get()); // Check the last module since duplicate imports, if any, will // probably be adjacent. Then just do a linear scan. if (UNLIKELY(last == child)) continue; int i = 0; while (i < n) { if (UNLIKELY(children.at(i).asCell() == child)) goto next; i += 1; } children.append(child); last = child; n += 1; next: { } } // Construct the array JSArray* array = JSC::constructArray(globalObject, static_cast(nullptr), children); mod->m_childrenValue.set(globalObject->vm(), mod, array); mod->m_children.clear(); return JSValue::encode(array); } return JSValue::encode(mod->m_childrenValue.get()); } JSC_DEFINE_CUSTOM_GETTER(getterLoaded, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!thisObject)) { return JSValue::encode(jsUndefined()); } return JSValue::encode(jsBoolean(thisObject->hasEvaluated)); } JSC_DEFINE_CUSTOM_SETTER(setterPaths, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->m_paths.set(globalObject->vm(), thisObject, JSValue::decode(value)); return true; } JSC_DEFINE_CUSTOM_SETTER(setterFilename, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->m_filename.set(globalObject->vm(), thisObject, JSValue::decode(value).toString(globalObject)); return true; } JSC_DEFINE_CUSTOM_SETTER(setterId, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->m_id.set(globalObject->vm(), thisObject, JSValue::decode(value).toString(globalObject)); return true; } JSC_DEFINE_CUSTOM_SETTER(setterParent, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; JSValue decodedValue = JSValue::decode(value); if (auto* parent = jsDynamicCast(decodedValue)) { thisObject->m_parent = parent; thisObject->m_overriddenParent.clear(); } else { thisObject->m_parent = {}; } return true; } JSC_DEFINE_CUSTOM_SETTER(setterLoaded, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->hasEvaluated = JSValue::decode(value).toBoolean(globalObject); return true; } JSC_DEFINE_HOST_FUNCTION(functionJSCommonJSModule_compile, (JSGlobalObject * globalObject, CallFrame* callframe)) { auto* moduleObject = jsDynamicCast(callframe->thisValue()); if (!moduleObject) { return JSValue::encode(jsUndefined()); } auto& vm = JSC::getVM(globalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); String sourceString = callframe->argument(0).toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); JSValue filenameValue = callframe->argument(1); String filenameString = filenameValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); String wrappedString; auto* zigGlobalObject = jsCast(globalObject); if (UNLIKELY(zigGlobalObject->hasOverriddenModuleWrapper)) { wrappedString = makeString( zigGlobalObject->m_moduleWrapperStart, sourceString, zigGlobalObject->m_moduleWrapperEnd); } else { wrappedString = makeString( "(function(exports,require,module,__filename,__dirname){"_s, sourceString, "\n})"_s); } moduleObject->sourceCode = makeSource( WTFMove(wrappedString), SourceOrigin(URL::fileURLWithFileSystemPath(filenameString)), JSC::SourceTaintedOrigin::Untainted, filenameString, WTF::TextPosition(), JSC::SourceProviderSourceType::Program); EncodedJSValue encodedFilename = JSValue::encode(filenameValue); #if OS(WINDOWS) JSValue dirnameValue = JSValue::decode(Bun__Path__dirname(globalObject, true, &encodedFilename, 1)); #else JSValue dirnameValue = JSValue::decode(Bun__Path__dirname(globalObject, false, &encodedFilename, 1)); #endif RETURN_IF_EXCEPTION(throwScope, {}); String dirnameString = dirnameValue.toWTFString(globalObject); WTF::NakedPtr exception; evaluateCommonJSModuleOnce( vm, jsCast(globalObject), moduleObject, jsString(vm, dirnameString), jsString(vm, filenameString)); RETURN_IF_EXCEPTION(throwScope, {}); return JSValue::encode(jsUndefined()); } static const struct HashTableValue JSCommonJSModulePrototypeTableValues[] = { { "_compile"_s, static_cast(PropertyAttribute::Function | PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::NativeFunctionType, functionJSCommonJSModule_compile, 2 } }, { "children"_s, static_cast(PropertyAttribute::CustomAccessor | PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, getterChildren, setterChildren } }, { "filename"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterFilename, setterFilename } }, { "id"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterId, setterId } }, { "loaded"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterLoaded, setterLoaded } }, { "parent"_s, static_cast(PropertyAttribute::CustomAccessor | PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, getterParent, setterParent } }, { "path"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterPath, setterPath } }, { "paths"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterPaths, setterPaths } }, }; class JSCommonJSModulePrototype final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; static JSCommonJSModulePrototype* create( JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) { JSCommonJSModulePrototype* prototype = new (NotNull, JSC::allocateCell(vm)) JSCommonJSModulePrototype(vm, structure); prototype->finishCreation(vm, globalObject); return prototype; } static JSC::Structure* createStructure( JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { auto* structure = JSC::Structure::create(vm, globalObject, prototype, TypeInfo(JSC::ObjectType, StructureFlags), info()); structure->setMayBePrototype(true); return structure; } DECLARE_INFO; JSCommonJSModulePrototype( JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) { } template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSCommonJSModulePrototype, Base); return &vm.plainObjectSpace(); } void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { Base::finishCreation(vm); ASSERT(inherits(info())); reifyStaticProperties(vm, info(), JSCommonJSModulePrototypeTableValues, *this); this->putDirectNativeFunction( vm, globalObject, clientData(vm)->builtinNames().requirePrivateName(), 2, jsFunctionRequireCommonJS, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete); this->putDirectNativeFunction( vm, globalObject, clientData(vm)->builtinNames().requireNativeModulePrivateName(), 0, jsFunctionRequireNativeModule, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete); } }; const JSC::ClassInfo JSCommonJSModulePrototype::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCommonJSModulePrototype) }; void JSCommonJSModule::finishCreation(JSC::VM& vm, JSC::JSString* id, JSValue filename, JSC::JSString* dirname, const JSC::SourceCode& sourceCode) { Base::finishCreation(vm); ASSERT(inherits(info())); m_id.set(vm, this, id); m_filename.set(vm, this, filename); m_dirname.set(vm, this, dirname); this->sourceCode = sourceCode; } JSC::Structure* JSCommonJSModule::createStructure( JSC::JSGlobalObject* globalObject) { auto& vm = JSC::getVM(globalObject); auto* prototype = JSCommonJSModulePrototype::create(vm, globalObject, JSCommonJSModulePrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); // Do not set the number of inline properties on this structure // there may be an off-by-one error in the Structure which causes `require.id` to become the require return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), NonArray); } JSCommonJSModule* JSCommonJSModule::create( JSC::VM& vm, JSC::Structure* structure, JSC::JSString* id, JSValue filename, JSC::JSString* dirname, const JSC::SourceCode& sourceCode) { JSCommonJSModule* cell = new (NotNull, JSC::allocateCell(vm)) JSCommonJSModule(vm, structure); cell->finishCreation(vm, id, filename, dirname, sourceCode); return cell; } JSC_DEFINE_HOST_FUNCTION(jsFunctionCreateCommonJSModule, (JSGlobalObject * globalObject, CallFrame* callframe)) { RELEASE_ASSERT(callframe->argumentCount() == 4); auto id = callframe->uncheckedArgument(0).toString(globalObject); JSValue object = callframe->uncheckedArgument(1); JSValue hasEvaluated = callframe->uncheckedArgument(2); ASSERT(hasEvaluated.isBoolean()); JSValue parent = callframe->uncheckedArgument(3); return JSValue::encode(JSCommonJSModule::create(jsCast(globalObject), id, object, hasEvaluated.isTrue(), parent)); } JSCommonJSModule* JSCommonJSModule::create( Zig::GlobalObject* globalObject, JSC::JSString* requireMapKey, JSValue exportsObject, bool hasEvaluated, JSValue parent) { auto& vm = JSC::getVM(globalObject); auto key = requireMapKey->value(globalObject); auto index = key->reverseFind(PLATFORM_SEP, key->length()); JSString* dirname; if (index != WTF::notFound) { dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index); } else { dirname = jsEmptyString(vm); } auto* out = JSCommonJSModule::create( vm, globalObject->CommonJSModuleObjectStructure(), requireMapKey, requireMapKey, dirname, SourceCode()); out->putDirect( vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), exportsObject, 0); out->hasEvaluated = hasEvaluated; if (parent && parent.isCell()) { if (auto* parentModule = jsDynamicCast(parent)) { out->m_parent = JSC::Weak(parentModule); } else { out->m_overriddenParent.set(vm, out, parent); } } else if (parent) { out->m_overriddenParent.set(vm, out, parent); } return out; } JSCommonJSModule* JSCommonJSModule::create( Zig::GlobalObject* globalObject, const WTF::String& key, JSValue exportsObject, bool hasEvaluated, JSValue parent) { auto& vm = JSC::getVM(globalObject); auto* requireMapKey = JSC::jsStringWithCache(vm, key); return JSCommonJSModule::create(globalObject, requireMapKey, exportsObject, hasEvaluated, parent); } size_t JSCommonJSModule::estimatedSize(JSC::JSCell* cell, JSC::VM& vm) { auto* thisObject = jsCast(cell); size_t additionalSize = 0; if (!thisObject->sourceCode.isNull() && !thisObject->sourceCode.view().isEmpty()) { additionalSize += thisObject->sourceCode.view().length(); if (!thisObject->sourceCode.view().is8Bit()) { additionalSize *= 2; } } return Base::estimatedSize(cell, vm) + additionalSize; } void JSCommonJSModule::destroy(JSC::JSCell* cell) { static_cast(cell)->JSCommonJSModule::~JSCommonJSModule(); } JSCommonJSModule::~JSCommonJSModule() { } void populateESMExports( JSC::JSGlobalObject* globalObject, JSValue result, Vector& exportNames, JSC::MarkedArgumentBuffer& exportValues, bool ignoreESModuleAnnotation) { auto& vm = JSC::getVM(globalObject); const Identifier& esModuleMarker = vm.propertyNames->__esModule; // Bun's interpretation of the "__esModule" annotation: // // - If a "default" export does not exist OR the __esModule annotation is not present, then we // set the default export to the exports object // // - If a "default" export also exists, then we set the default export // to the value of it (matching Babel behavior) // // https://stackoverflow.com/questions/50943704/whats-the-purpose-of-object-definepropertyexports-esmodule-value-0 // https://github.com/nodejs/node/issues/40891 // https://github.com/evanw/bundler-esm-cjs-tests // https://github.com/evanw/esbuild/issues/1591 // https://github.com/oven-sh/bun/issues/3383 // // Note that this interpretation is slightly different // // - We do not ignore when "type": "module" or when the file // extension is ".mjs". Build tools determine that based on the // caller's behavior, but in a JS runtime, there is only one ModuleNamespaceObject. // // It would be possible to match the behavior at runtime, but // it would need further engine changes which do not match the ES Module spec // // - We ignore the value of the annotation. We only look for the // existence of the value being set. This is for performance reasons, but also // this annotation is meant for tooling and the only usages of setting // it to something that does NOT evaluate to "true" I could find were in // unit tests of build tools. Happy to revisit this if users file an issue. bool needsToAssignDefault = true; if (auto* exports = result.getObject()) { bool hasESModuleMarker = false; if (!ignoreESModuleAnnotation) { auto catchScope = DECLARE_CATCH_SCOPE(vm); PropertySlot slot(exports, PropertySlot::InternalMethodType::VMInquiry, &vm); if (exports->getPropertySlot(globalObject, esModuleMarker, slot)) { JSValue value = slot.getValue(globalObject, esModuleMarker); if (!value.isUndefinedOrNull()) { if (value.pureToBoolean() == TriState::True) { hasESModuleMarker = true; } } } if (catchScope.exception()) { catchScope.clearException(); } } auto* structure = exports->structure(); uint32_t size = structure->inlineSize() + structure->outOfLineSize(); exportNames.reserveCapacity(size + 2); exportValues.ensureCapacity(size + 2); auto catchScope = DECLARE_CATCH_SCOPE(vm); if (catchScope.exception()) { catchScope.clearException(); } if (hasESModuleMarker) { if (canPerformFastEnumeration(structure)) { exports->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { auto key = entry.key(); if (key->isSymbol() || key == esModuleMarker) return true; needsToAssignDefault = needsToAssignDefault && key != vm.propertyNames->defaultKeyword; JSValue value = exports->getDirect(entry.offset()); exportNames.append(Identifier::fromUid(vm, key)); exportValues.append(value); return true; }); } else { JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude); exports->methodTable()->getOwnPropertyNames(exports, globalObject, properties, DontEnumPropertiesMode::Exclude); if (catchScope.exception()) { catchScope.clearExceptionExceptTermination(); return; } for (auto property : properties) { if (UNLIKELY(property.isEmpty() || property.isNull() || property == esModuleMarker || property.isPrivateName() || property.isSymbol())) continue; // ignore constructor if (property == vm.propertyNames->constructor) continue; JSC::PropertySlot slot(exports, PropertySlot::InternalMethodType::Get); if (!exports->getPropertySlot(globalObject, property, slot)) continue; // Allow DontEnum properties which are not getter/setters // https://github.com/oven-sh/bun/issues/4432 if (slot.attributes() & PropertyAttribute::DontEnum) { if (!(slot.isValue() || slot.isCustom())) { continue; } } exportNames.append(property); JSValue getterResult = slot.getValue(globalObject, property); // If it throws, we keep them in the exports list, but mark it as undefined // This is consistent with what Node.js does. if (catchScope.exception()) { catchScope.clearException(); getterResult = jsUndefined(); } exportValues.append(getterResult); needsToAssignDefault = needsToAssignDefault && property != vm.propertyNames->defaultKeyword; } } } else if (canPerformFastEnumeration(structure)) { exports->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { auto key = entry.key(); if (key->isSymbol() || key == vm.propertyNames->defaultKeyword) return true; JSValue value = exports->getDirect(entry.offset()); exportNames.append(Identifier::fromUid(vm, key)); exportValues.append(value); return true; }); } else { JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude); exports->methodTable()->getOwnPropertyNames(exports, globalObject, properties, DontEnumPropertiesMode::Include); if (catchScope.exception()) { catchScope.clearExceptionExceptTermination(); return; } for (auto property : properties) { if (UNLIKELY(property.isEmpty() || property.isNull() || property == vm.propertyNames->defaultKeyword || property.isPrivateName() || property.isSymbol())) continue; // ignore constructor if (property == vm.propertyNames->constructor) continue; JSC::PropertySlot slot(exports, PropertySlot::InternalMethodType::Get); if (!exports->getPropertySlot(globalObject, property, slot)) continue; if (slot.attributes() & PropertyAttribute::DontEnum) { // Allow DontEnum properties which are not getter/setters // https://github.com/oven-sh/bun/issues/4432 if (!(slot.isValue() || slot.isCustom())) { continue; } } exportNames.append(property); JSValue getterResult = slot.getValue(globalObject, property); // If it throws, we keep them in the exports list, but mark it as undefined // This is consistent with what Node.js does. if (catchScope.exception()) { catchScope.clearException(); getterResult = jsUndefined(); } exportValues.append(getterResult); } } } if (needsToAssignDefault) { exportNames.append(vm.propertyNames->defaultKeyword); exportValues.append(result); } } void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject, const JSC::Identifier& moduleKey, Vector& exportNames, JSC::MarkedArgumentBuffer& exportValues) { auto result = this->exportsObject(); populateESMExports(globalObject, result, exportNames, exportValues, this->ignoreESModuleAnnotation); } void JSCommonJSModule::setExportsObject(JSC::JSValue exportsObject) { this->putDirect(vm(), JSC::PropertyName(clientData(vm())->builtinNames().exportsPublicName()), exportsObject, 0); } Structure* createCommonJSModuleStructure( Zig::GlobalObject* globalObject) { return JSCommonJSModule::createStructure(globalObject); } template void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) { JSCommonJSModule* thisObject = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); Base::visitChildren(thisObject, visitor); // Use appendHidden so it doesn't show up in the heap snapshot twice. visitor.appendHidden(thisObject->m_id); visitor.appendHidden(thisObject->m_filename); visitor.appendHidden(thisObject->m_dirname); visitor.appendHidden(thisObject->m_paths); visitor.appendHidden(thisObject->m_overriddenParent); visitor.appendHidden(thisObject->m_childrenValue); visitor.appendValues(thisObject->m_children.data(), thisObject->m_children.size()); } DEFINE_VISIT_CHILDREN(JSCommonJSModule); void JSCommonJSModule::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) { auto* thisObject = jsCast(cell); analyzer.setLabelForCell(cell, "Module (CommonJS)"_s); Base::analyzeHeap(cell, analyzer); auto& vm = cell->vm(); auto& builtinNames = Bun::builtinNames(vm); if (auto* id = thisObject->m_id.get()) { analyzer.analyzePropertyNameEdge(cell, id, vm.propertyNames->id.impl()); } if (thisObject->m_filename) { JSValue filename = thisObject->m_filename.get(); if (filename.isCell()) { analyzer.analyzePropertyNameEdge(cell, filename.asCell(), builtinNames.filenamePublicName().impl()); } } if (thisObject->m_dirname) { JSValue dirname = thisObject->m_dirname.get(); if (dirname.isCell()) { analyzer.analyzePropertyNameEdge(cell, dirname.asCell(), builtinNames.dirnamePublicName().impl()); } } if (thisObject->m_paths) { JSValue paths = thisObject->m_paths.get(); if (paths.isCell()) { analyzer.analyzePropertyNameEdge(cell, paths.asCell(), builtinNames.pathsPublicName().impl()); } } if (thisObject->m_overriddenParent) { JSValue overriddenParent = thisObject->m_overriddenParent.get(); if (overriddenParent.isCell()) { const Identifier overriddenParentIdentifier = Identifier::fromString(vm, "parent"_s); analyzer.analyzePropertyNameEdge(cell, overriddenParent.asCell(), overriddenParentIdentifier.impl()); } } } const JSC::ClassInfo JSCommonJSModule::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCommonJSModule) }; 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) }; // JSCommonJSModule.$require(resolvedId, newModule, userArgumentCount, userOptions) JSC_DEFINE_HOST_FUNCTION(jsFunctionRequireCommonJS, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe)) { auto* globalObject = jsCast(lexicalGlobalObject); auto& vm = JSC::getVM(globalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); ASSERT(callframe->argumentCount() == 4); // If overriddenRequire is called with invalid this, execution could potentially reach here. JSCommonJSModule* referrerModule = jsDynamicCast(callframe->thisValue()); if (!referrerModule) return throwVMTypeError(globalObject, throwScope); JSValue specifierValue = callframe->uncheckedArgument(0); // If Module._resolveFilename is overridden, this could cause this to be a non-string WTF::String specifier = specifierValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); // If this.filename is overridden, this could cause this to be a non-string WTF::String referrer = referrerModule->filename().toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); // This is always a new JSCommonJSModule object; cast cannot fail. JSCommonJSModule* child = jsCast(callframe->uncheckedArgument(1)); BunString specifierStr = Bun::toStringNonRef(specifier); BunString referrerStr = Bun::toStringNonRef(referrer); BunString typeAttributeStr = { BunStringTag::Dead }; String typeAttribute = String(); // We need to be able to wire in the "type" import attribute from bundled code.. // So we do it via CommonJS require(). // $argumentCount() always returns a Int32 JSValue int32_t userArgumentCount = callframe->argument(2).asInt32(); // If they called require(id), skip the check for the type attribute if (UNLIKELY(userArgumentCount >= 2)) { JSValue options = callframe->uncheckedArgument(3); if (options.isObject()) { JSObject* obj = options.getObject(); // This getter is expensive and rare. if (auto typeValue = obj->getIfPropertyExists(globalObject, vm.propertyNames->type)) { if (typeValue.isString()) { typeAttribute = typeValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); typeAttributeStr = Bun::toStringNonRef(typeAttribute); } } RETURN_IF_EXCEPTION(throwScope, {}); } } // Load the module JSValue fetchResult = Bun::fetchCommonJSModule( globalObject, child, specifierValue, &specifierStr, &referrerStr, LIKELY(typeAttribute.isEmpty()) ? nullptr : &typeAttributeStr); if (auto exception = throwScope.exception()) { throwScope.clearException(); // On error, remove the module from the require map/ // so that it can be re-evaluated on the next require. bool wasRemoved = globalObject->requireMap()->remove(globalObject, specifierValue); ASSERT(wasRemoved); throwScope.throwException(globalObject, exception); RELEASE_AND_RETURN(throwScope, {}); } RELEASE_AND_RETURN(throwScope, JSValue::encode(fetchResult)); } JSC_DEFINE_HOST_FUNCTION(jsFunctionRequireNativeModule, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe)) { auto* globalObject = jsCast(lexicalGlobalObject); auto& vm = JSC::getVM(globalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); JSCommonJSModule* thisObject = jsDynamicCast(callframe->thisValue()); if (!thisObject) return throwVMTypeError(globalObject, throwScope); JSValue specifierValue = callframe->argument(0); WTF::String specifier = specifierValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); ErrorableResolvedSource res; BunString specifierStr = Bun::toStringNonRef(specifier); if (auto result = fetchBuiltinModuleWithoutResolution(globalObject, &specifierStr, &res)) { if (res.success) return JSC::JSValue::encode(result); } ASSERT_WITH_MESSAGE(false, "Failed to fetch builtin module %s", specifier.utf8().data()); return throwVMError(globalObject, throwScope, "Failed to fetch builtin module"_s); } void RequireResolveFunctionPrototype::finishCreation(JSC::VM& vm) { Base::finishCreation(vm); ASSERT(inherits(info())); reifyStaticProperties(vm, info(), RequireResolveFunctionPrototypeValues, *this); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } void JSCommonJSModule::evaluate( Zig::GlobalObject* globalObject, const WTF::String& key, ResolvedSource& source, bool isBuiltIn) { auto& vm = JSC::getVM(globalObject); if (UNLIKELY(globalObject->hasOverriddenModuleWrapper)) { auto string = source.source_code.toWTFString(BunString::ZeroCopy); auto trimStart = string.find('\n'); if (trimStart != WTF::notFound) { if (source.needsDeref && !isBuiltIn) { source.needsDeref = false; source.source_code.deref(); } auto wrapperStart = globalObject->m_moduleWrapperStart; auto wrapperEnd = globalObject->m_moduleWrapperEnd; source.source_code = Bun::toStringRef(makeString( wrapperStart, string.substring(trimStart, string.length() - trimStart - 4), wrapperEnd)); source.needsDeref = true; } } auto sourceProvider = Zig::SourceProvider::create(jsCast(globalObject), source, JSC::SourceProviderSourceType::Program, isBuiltIn); this->ignoreESModuleAnnotation = source.tag == ResolvedSourceTagPackageJSONTypeModule; if (this->hasEvaluated) return; this->sourceCode = JSC::SourceCode(WTFMove(sourceProvider)); evaluateCommonJSModuleOnce(vm, globalObject, this, this->m_dirname.get(), this->m_filename.get()); return; } std::optional createCommonJSModule( Zig::GlobalObject* globalObject, JSString* requireMapKey, ResolvedSource& source, bool isBuiltIn) { JSCommonJSModule* moduleObject = nullptr; WTF::String sourceURL = source.source_url.toWTFString(); JSValue entry = globalObject->requireMap()->get(globalObject, requireMapKey); bool ignoreESModuleAnnotation = source.tag == ResolvedSourceTagPackageJSONTypeModule; SourceOrigin sourceOrigin; if (entry) { moduleObject = jsDynamicCast(entry); } if (!moduleObject) { VM& vm = JSC::getVM(globalObject); size_t index = sourceURL.reverseFind(PLATFORM_SEP, sourceURL.length()); JSString* dirname; JSString* filename = requireMapKey; if (index != WTF::notFound) { dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index); } else { dirname = jsEmptyString(vm); } auto requireMap = globalObject->requireMap(); if (requireMap->size() == 0) { requireMapKey = JSC::jsString(vm, WTF::String("."_s)); } if (UNLIKELY(globalObject->hasOverriddenModuleWrapper)) { auto concat = makeString( globalObject->m_moduleWrapperStart, source.source_code.toWTFString(BunString::ZeroCopy), globalObject->m_moduleWrapperEnd); source.source_code.deref(); source.source_code = Bun::toStringRef(concat); } auto sourceProvider = Zig::SourceProvider::create(jsCast(globalObject), source, JSC::SourceProviderSourceType::Program, isBuiltIn); sourceOrigin = sourceProvider->sourceOrigin(); moduleObject = JSCommonJSModule::create( vm, globalObject->CommonJSModuleObjectStructure(), requireMapKey, filename, dirname, WTFMove(JSC::SourceCode(WTFMove(sourceProvider)))); moduleObject->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), JSC::constructEmptyObject(globalObject, globalObject->objectPrototype()), 0); requireMap->set(globalObject, filename, moduleObject); } else { sourceOrigin = Zig::toSourceOrigin(sourceURL, isBuiltIn); } moduleObject->ignoreESModuleAnnotation = ignoreESModuleAnnotation; return JSC::SourceCode( JSC::SyntheticSourceProvider::create( [](JSC::JSGlobalObject* lexicalGlobalObject, const JSC::Identifier& moduleKey, Vector& exportNames, JSC::MarkedArgumentBuffer& exportValues) -> void { auto* globalObject = jsCast(lexicalGlobalObject); auto& vm = JSC::getVM(globalObject); JSValue keyValue = identifierToJSValue(vm, moduleKey); JSValue entry = globalObject->requireMap()->get(globalObject, keyValue); if (entry) { if (auto* moduleObject = jsDynamicCast(entry)) { if (!moduleObject->hasEvaluated) { auto scope = DECLARE_THROW_SCOPE(vm); evaluateCommonJSModuleOnce( vm, globalObject, moduleObject, moduleObject->m_dirname.get(), moduleObject->m_filename.get()); if (auto exception = scope.exception()) { scope.clearException(); // On error, remove the module from the require map // so that it can be re-evaluated on the next require. globalObject->requireMap()->remove(globalObject, moduleObject->filename()); scope.throwException(globalObject, exception); return; } } moduleObject->toSyntheticSource(globalObject, moduleKey, exportNames, exportValues); } } else { // require map was cleared of the entry } }, sourceOrigin, sourceURL)); } JSObject* JSCommonJSModule::createBoundRequireFunction(VM& vm, JSGlobalObject* lexicalGlobalObject, const WTF::String& pathString) { ASSERT(!pathString.startsWith("file://"_s)); auto* globalObject = jsCast(lexicalGlobalObject); JSString* filename = JSC::jsStringWithCache(vm, pathString); auto index = pathString.reverseFind(PLATFORM_SEP, pathString.length()); JSString* dirname; if (index != WTF::notFound) { dirname = JSC::jsSubstring(globalObject, filename, 0, index); } else { dirname = jsEmptyString(vm); } auto moduleObject = Bun::JSCommonJSModule::create( vm, globalObject->CommonJSModuleObjectStructure(), filename, filename, dirname, SourceCode()); JSFunction* requireFunction = JSC::JSBoundFunction::create(vm, globalObject, globalObject->requireFunctionUnbound(), moduleObject, ArgList(), 1, globalObject->commonStrings().requireString(globalObject)); JSFunction* resolveFunction = JSC::JSBoundFunction::create(vm, globalObject, globalObject->requireResolveFunctionUnbound(), moduleObject->filename(), ArgList(), 1, globalObject->commonStrings().resolveString(globalObject)); requireFunction->putDirect(vm, vm.propertyNames->resolve, resolveFunction, 0); return requireFunction; } } // namespace Bun