aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-06-24 06:59:47 -0700
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-06-24 06:59:47 -0700
commit7bb75f55530e52447b9c68bc5b0908bf734ba184 (patch)
treee30b431d6f257824f2821c56a2ec01136938cc5e /src/bun.js
parent6d6a89780b10816de38c465b1e6bb583979feacd (diff)
downloadbun-7bb75f55530e52447b9c68bc5b0908bf734ba184.tar.gz
bun-7bb75f55530e52447b9c68bc5b0908bf734ba184.tar.zst
bun-7bb75f55530e52447b9c68bc5b0908bf734ba184.zip
Add dynamic require support
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp109
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h2
-rw-r--r--src/bun.js/builtins/BunBuiltinNames.h6
-rw-r--r--src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp122
-rw-r--r--src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h16
-rw-r--r--src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h82
-rw-r--r--src/bun.js/builtins/js/JSZigGlobalObject.js131
-rw-r--r--src/bun.js/javascript.zig77
-rw-r--r--src/bun.js/module.exports.js24
9 files changed, 500 insertions, 69 deletions
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index e99f9a1cc..a51bc433b 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -328,13 +328,13 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure)
: JSC::JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
+ , m_bunVM(Bun__getVM())
, m_constructors(makeUnique<WebCore::DOMConstructors>())
, m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal))
, m_worldIsNormal(true)
, m_builtinInternalFunctions(vm)
{
- m_bunVM = Bun__getVM();
m_scriptExecutionContext = new WebCore::ScriptExecutionContext(&vm, this);
}
@@ -379,6 +379,8 @@ void GlobalObject::setConsole(void* console)
#pragma mark - Globals
+static JSC_DECLARE_HOST_FUNCTION(functionFulfillModuleSync);
+
JSC_DECLARE_CUSTOM_GETTER(functionLazyLoadStreamProtoypeMap_getter);
JSC_DEFINE_CUSTOM_GETTER(functionLazyLoadStreamProtoypeMap_getter,
@@ -1714,7 +1716,38 @@ void GlobalObject::finishCreation(VM& vm)
auto* prototype = createJSSinkControllerPrototype(init.vm, init.owner, WebCore::SinkID::ArrayBufferSink);
init.set(prototype);
});
+ m_importMetaObjectStructure.initLater(
+ [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSObject>::Initializer& init) {
+ JSC::JSObject* metaProperties = JSC::constructEmptyObject(init.owner, init.owner->objectPrototype(), 10);
+ auto& vm = init.vm;
+ auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(init.owner);
+ auto clientData = WebCore::clientData(vm);
+ metaProperties->putDirect(vm, clientData->builtinNames().filePublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().dirPublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().mainPublicName(), jsBoolean(false), 0);
+ metaProperties->putDirectBuiltinFunction(vm, globalObject,
+ clientData->builtinNames().requirePublicName(), jsZigGlobalObjectRequireCodeGenerator(vm), 0);
+ metaProperties->putDirectBuiltinFunction(vm, globalObject,
+ clientData->builtinNames().loadModulePublicName(), jsZigGlobalObjectLoadModuleCodeGenerator(vm), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
+ metaProperties->putDirectBuiltinFunction(vm, globalObject,
+ clientData->builtinNames().requireModulePublicName(), jsZigGlobalObjectRequireModuleCodeGenerator(vm), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
+
+ metaProperties->putDirectNativeFunction(vm, globalObject, clientData->builtinNames().resolvePublicName(), 1,
+ functionImportMeta__resolve,
+ NoIntrinsic,
+ JSC::PropertyAttribute::Function | 0);
+ metaProperties->putDirectNativeFunction(
+ vm, globalObject, clientData->builtinNames().resolveSyncPublicName(),
+ 1,
+ functionImportMeta__resolveSync,
+ NoIntrinsic,
+ JSC::PropertyAttribute::Function | 0);
+
+ init.set(metaProperties);
+ });
m_lazyReadableStreamPrototypeMap.initLater(
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSMap>::Initializer& init) {
auto* map = JSC::JSMap::create(init.owner, init.vm, init.owner->mapStructure());
@@ -1751,7 +1784,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
auto& builtinNames = WebCore::builtinNames(vm);
WTF::Vector<GlobalPropertyInfo> extraStaticGlobals;
- extraStaticGlobals.reserveCapacity(29);
+ extraStaticGlobals.reserveCapacity(30);
JSC::Identifier queueMicrotaskIdentifier = JSC::Identifier::fromString(vm, "queueMicrotask"_s);
extraStaticGlobals.uncheckedAppend(
@@ -1844,7 +1877,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.isAbortSignalPrivateName(), JSFunction::create(vm, this, 1, String(), isAbortSignal), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.getInternalWritableStreamPrivateName(), JSFunction::create(vm, this, 1, String(), getInternalWritableStream), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.createWritableStreamFromInternalPrivateName(), JSFunction::create(vm, this, 1, String(), createWritableStreamFromInternal), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
-
+ extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.fulfillModuleSyncPrivateName(), JSFunction::create(vm, this, 1, String(), functionFulfillModuleSync), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::Function));
this->addStaticGlobals(extraStaticGlobals.data(), extraStaticGlobals.size());
extraStaticGlobals.releaseBuffer();
@@ -1856,6 +1889,9 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
putDirectBuiltinFunction(vm, this, builtinNames.readableStreamToArrayPrivateName(), readableStreamReadableStreamToArrayCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
putDirectBuiltinFunction(vm, this, builtinNames.assignDirectStreamPrivateName(), readableStreamInternalsAssignDirectStreamCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ // putDirectBuiltinFunction(vm, this, builtinNames.loadModulePrivateName(), jsZigGlobalObjectInternalsLoadModuleCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ // putDirectBuiltinFunction(vm, this, builtinNames.requireModulePrivateName(), jsZigGlobalObjectInternalsRequireModuleCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+
putDirectNativeFunction(vm, this, builtinNames.createUninitializedArrayBufferPrivateName(), 1, functionCreateUninitializedArrayBuffer, NoIntrinsic, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::Function);
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "process"_s), JSC::CustomGetterSetter::create(vm, property_lazyProcessGetter, property_lazyProcessSetter),
@@ -2096,6 +2132,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_JSFFIFunctionStructure.visit(visitor);
thisObject->m_JSArrayBufferSinkClassStructure.visit(visitor);
thisObject->m_JSArrayBufferControllerPrototype.visit(visitor);
+ thisObject->m_importMetaObjectStructure.visit(visitor);
thisObject->m_lazyReadableStreamPrototypeMap.visit(visitor);
visitor.append(thisObject->m_readableStreamToArrayBufferResolve);
@@ -2200,6 +2237,40 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* g
return result;
}
+static JSC_DEFINE_HOST_FUNCTION(functionFulfillModuleSync,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSC::JSValue key = callFrame->argument(0);
+
+ auto moduleKey = key.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(scope, JSValue::encode(JSC::jsUndefined()));
+
+ if (moduleKey.endsWith(".node"_s)) {
+ throwException(globalObject, scope, createTypeError(globalObject, "To load Node-API modules, use require() or process.dlopen instead of importSync."_s));
+ return JSValue::encode(JSC::jsUndefined());
+ }
+
+ auto specifier = Zig::toZigString(moduleKey);
+ ErrorableResolvedSource res;
+ res.success = false;
+ res.result.err.code = 0;
+ res.result.err.ptr = nullptr;
+
+ Zig__GlobalObject__fetch(&res, globalObject, &specifier, &specifier);
+
+ if (!res.success) {
+ throwException(scope, res.result.err, globalObject);
+ return JSValue::encode(JSC::jsUndefined());
+ }
+
+ auto provider = Zig::SourceProvider::create(res.result.value);
+ globalObject->moduleLoader()->provideFetch(globalObject, key, JSC::SourceCode(provider));
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
+ RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
+}
+
JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject,
JSModuleLoader* loader, JSValue key,
JSValue value1, JSValue value2)
@@ -2275,14 +2346,16 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
- JSC::JSObject* metaProperties = JSC::constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure());
+ JSC::JSObject* metaProperties = JSC::constructEmptyObject(globalObject, reinterpret_cast<GlobalObject*>(globalObject)->ImportMetaObjectPrototype());
RETURN_IF_EXCEPTION(scope, nullptr);
auto clientData = WebCore::clientData(vm);
JSString* keyString = key.toStringOrNull(globalObject);
if (UNLIKELY(!keyString)) {
- return metaProperties;
+ RELEASE_AND_RETURN(scope, metaProperties);
}
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
auto view = keyString->value(globalObject);
auto index = view.reverseFind('/', view.length());
if (index != WTF::notFound) {
@@ -2293,28 +2366,16 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
JSC::jsSubstring(globalObject, keyString, index + 1, keyString->length() - index - 1));
} else {
metaProperties->putDirect(vm, clientData->builtinNames().filePublicName(), keyString);
- metaProperties->putDirect(vm, clientData->builtinNames().dirPublicName(), jsEmptyString(vm));
}
- metaProperties->putDirect(vm, clientData->builtinNames().resolvePublicName(),
- JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject), 0,
- clientData->builtinNames().resolvePublicName().string(), functionImportMeta__resolve),
- JSC::PropertyAttribute::Function | 0);
- metaProperties->putDirect(vm, clientData->builtinNames().resolveSyncPublicName(),
- JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject), 0,
- clientData->builtinNames().resolveSyncPublicName().string(), functionImportMeta__resolveSync),
- JSC::PropertyAttribute::Function | 0);
-
- metaProperties->putDirectBuiltinFunction(vm, globalObject, clientData->builtinNames().requirePublicName(),
- jsZigGlobalObjectRequireCodeGenerator(vm),
- JSC::PropertyAttribute::Builtin | 0);
-
- metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), key);
- metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), JSC::JSValue(JSC::jsString(vm, WTF::URL::fileURLWithFileSystemPath(view).string())));
-
- RETURN_IF_EXCEPTION(scope, nullptr);
+ metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), keyString);
+ if (view.startsWith('/')) {
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), JSC::JSValue(JSC::jsString(vm, WTF::URL::fileURLWithFileSystemPath(view).string())));
+ } else {
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), keyString);
+ }
- return metaProperties;
+ RELEASE_AND_RETURN(scope, metaProperties);
}
JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* globalObject,
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 41556e639..cabaf57a9 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -153,6 +153,7 @@ public:
WebCore::JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; }
JSC::Structure* FFIFunctionStructure() { return m_JSFFIFunctionStructure.getInitializedOnMainThread(this); }
JSC::Structure* NapiClassStructure() { return m_NapiClassStructure.getInitializedOnMainThread(this); }
+ JSC::JSObject* ImportMetaObjectPrototype() { return m_importMetaObjectStructure.getInitializedOnMainThread(this); }
JSC::Structure* ArrayBufferSinkStructure() { return m_JSArrayBufferSinkClassStructure.getInitializedOnMainThread(this); }
JSC::JSObject* ArrayBufferSink() { return m_JSArrayBufferSinkClassStructure.constructorInitializedOnMainThread(this); }
JSC::JSValue ArrayBufferSinkPrototype() { return m_JSArrayBufferSinkClassStructure.prototypeInitializedOnMainThread(this); }
@@ -183,6 +184,7 @@ private:
LazyClassStructure m_NapiClassStructure;
LazyClassStructure m_JSArrayBufferSinkClassStructure;
LazyProperty<JSGlobalObject, JSObject> m_JSArrayBufferControllerPrototype;
+ LazyProperty<JSGlobalObject, JSObject> m_importMetaObjectStructure;
LazyProperty<JSGlobalObject, JSMap> m_lazyReadableStreamPrototypeMap;
DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock);
diff --git a/src/bun.js/builtins/BunBuiltinNames.h b/src/bun.js/builtins/BunBuiltinNames.h
index 1b49338c0..6acd30a8b 100644
--- a/src/bun.js/builtins/BunBuiltinNames.h
+++ b/src/bun.js/builtins/BunBuiltinNames.h
@@ -101,6 +101,7 @@ using namespace JSC;
macro(flush) \
macro(flushAlgorithm) \
macro(format) \
+ macro(fulfillModuleSync) \
macro(get) \
macro(getInternalWritableStream) \
macro(handleEvent) \
@@ -125,9 +126,11 @@ using namespace JSC;
macro(join) \
macro(kind) \
macro(lazy) \
- macro(lazyStreamPrototypeMap) \
macro(lazyLoad) \
+ macro(lazyStreamPrototypeMap) \
+ macro(loadModule) \
macro(localStreams) \
+ macro(main) \
macro(makeDOMException) \
macro(makeGetterTypeError) \
macro(makeThisTypeError) \
@@ -174,6 +177,7 @@ using namespace JSC;
macro(releaseLock) \
macro(removeEventListener) \
macro(require) \
+ macro(requireModule) \
macro(resolve) \
macro(resolveSync) \
macro(resume) \
diff --git a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp
index 05863cc27..421ba1181 100644
--- a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp
+++ b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp
@@ -49,7 +49,7 @@ namespace WebCore {
const JSC::ConstructAbility s_jsZigGlobalObjectRequireCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_jsZigGlobalObjectRequireCodeConstructorKind = JSC::ConstructorKind::None;
-const int s_jsZigGlobalObjectRequireCodeLength = 1225;
+const int s_jsZigGlobalObjectRequireCodeLength = 1221;
static const JSC::Intrinsic s_jsZigGlobalObjectRequireCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_jsZigGlobalObjectRequireCode =
"(function (name) {\n" \
@@ -57,7 +57,7 @@ const char* const s_jsZigGlobalObjectRequireCode =
" if (typeof name !== \"string\") {\n" \
" @throwTypeError(\"require() expects a string as its argument\");\n" \
" }\n" \
- "\n" \
+ " \n" \
" const resolved = this.resolveSync(name, this.path);\n" \
" var requireCache = (globalThis[Symbol.for(\"_requireCache\")] ||= new @Map);\n" \
" var cached = requireCache.@get(resolved);\n" \
@@ -69,6 +69,7 @@ const char* const s_jsZigGlobalObjectRequireCode =
" return cached;\n" \
" }\n" \
"\n" \
+ "\n" \
" //\n" \
" if (resolved.endsWith(\".json\")) {\n" \
" var fs = (globalThis[Symbol.for(\"_fs\")] ||= Bun.fs());\n" \
@@ -85,9 +86,124 @@ const char* const s_jsZigGlobalObjectRequireCode =
" var exports = Bun.TOML.parse(fs.readFileSync(resolved, \"utf8\"));\n" \
" requireCache.@set(resolved, exports);\n" \
" return exports;\n" \
+ " } else {\n" \
+ " var exports = this.requireModule(this, resolved);\n" \
+ " requireCache.@set(resolved, exports);\n" \
+ " return exports;\n" \
+ " }\n" \
+ "})\n" \
+;
+
+const JSC::ConstructAbility s_jsZigGlobalObjectLoadModuleCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_jsZigGlobalObjectLoadModuleCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_jsZigGlobalObjectLoadModuleCodeLength = 2783;
+static const JSC::Intrinsic s_jsZigGlobalObjectLoadModuleCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_jsZigGlobalObjectLoadModuleCode =
+ "(function (meta, resolvedSpecifier) {\n" \
+ " \"use strict\";\n" \
+ " var queue = @createFIFO();\n" \
+ " var key = resolvedSpecifier;\n" \
+ " \n" \
+ " var Loader = globalThis.Loader;\n" \
+ " var registry = Loader.registry;\n" \
+ " while (key) {\n" \
+ " @fulfillModuleSync(key);\n" \
+ " var entry = registry.@get(key);\n" \
+ "\n" \
+ " //\n" \
+ " //\n" \
+ " //\n" \
+ " //\n" \
+ " var sourceCodeObject = @getPromiseInternalField(entry.fetch, @promiseFieldReactionsOrResult);\n" \
+ " \n" \
+ "\n" \
+ " //\n" \
+ " //\n" \
+ " //\n" \
+ " var moduleRecordPromise = Loader.parseModule(key, sourceCodeObject);\n" \
+ " var module = entry.module;\n" \
+ " if (!module && moduleRecordPromise && @isPromise(moduleRecordPromise)) {\n" \
+ " var reactionsOrResult = @getPromiseInternalField(moduleRecordPromise, @promiseFieldReactionsOrResult);\n" \
+ " var flags = @getPromiseInternalField(moduleRecordPromise, @promiseFieldFlags);\n" \
+ " var state = flags & @promiseStateMask;\n" \
+ "\n" \
+ " //\n" \
+ " if (state === @promiseStatePending || (reactionsOrResult && @isPromise(reactionsOrResult))) {\n" \
+ " @throwTypeError(`require() async module \\\"${key}\\\" is unsupported`);\n" \
+ " \n" \
+ " } else if (state === @promiseStateRejected) {\n" \
+ " //\n" \
+ " //\n" \
+ " @throwTypeError(`${reactionsOrResult?.message ?? \"An error occurred\"} while parsing module \\\"${key}\\\"`);\n" \
+ " }\n" \
+ " entry.module = module = reactionsOrResult;\n" \
+ " } else if (moduleRecordPromise && !module) {\n" \
+ " entry.module = module = moduleRecordPromise;\n" \
+ " }\n" \
+ "\n" \
+ " //\n" \
+ " @setStateToMax(entry, @ModuleLink);\n" \
+ " var dependenciesMap = module.dependenciesMap;\n" \
+ " var requestedModules = Loader.requestedModules(module);\n" \
+ " var dependencies = @newArrayWithSize(requestedModules.length);\n" \
+ " \n" \
+ " for (var i = 0, length = requestedModules.length; i < length; ++i) {\n" \
+ " var depName = requestedModules[i];\n" \
+ "\n" \
+ " //\n" \
+ " //\n" \
+ " var depKey = depName[0] === '/' ? depName : Loader.resolveSync(depName, key, @undefined);\n" \
+ " var depEntry = Loader.ensureRegistered(depKey);\n" \
+ "\n" \
+ " if (depEntry.state < @ModuleLink) {\n" \
+ " queue.push(depKey);\n" \
+ " }\n" \
+ "\n" \
+ " @putByValDirect(dependencies, i, depEntry);\n" \
+ " dependenciesMap.@set(depName, depEntry);\n" \
+ " }\n" \
+ "\n" \
+ " entry.dependencies = dependencies;\n" \
+ " key = queue.shift();\n" \
+ " while (key && ((registry.@get(key)?.state ?? @ModuleFetch) >= @ModuleLink)) {\n" \
+ " key = queue.shift();\n" \
+ " }\n" \
+ " }\n" \
+ "\n" \
+ " var linkAndEvaluateResult = Loader.linkAndEvaluateModule(resolvedSpecifier, @undefined);\n" \
+ " if (linkAndEvaluateResult && @isPromise(linkAndEvaluateResult)) {\n" \
+ " //\n" \
+ " //\n" \
+ " @throwTypeError(`require() async module \\\"${resolvedSpecifier}\\\" is unsupported`);\n" \
+ " }\n" \
+ "\n" \
+ " return Loader.registry.@get(resolvedSpecifier);\n" \
+ "})\n" \
+;
+
+const JSC::ConstructAbility s_jsZigGlobalObjectRequireModuleCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_jsZigGlobalObjectRequireModuleCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_jsZigGlobalObjectRequireModuleCodeLength = 613;
+static const JSC::Intrinsic s_jsZigGlobalObjectRequireModuleCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_jsZigGlobalObjectRequireModuleCode =
+ "(function (meta, resolved) {\n" \
+ " \"use strict\";\n" \
+ " var Loader = globalThis.Loader;\n" \
+ " var entry = Loader.registry.@get(resolved);\n" \
+ "\n" \
+ " if (!entry || !entry.evaluated) {\n" \
+ " entry = this.loadModule(meta, resolved); \n" \
" }\n" \
"\n" \
- " @throwTypeError(`Dynamic require isn't supported for file type: ${resolved.subsring(resolved.lastIndexOf(\".\") + 1) || resolved}`);\n" \
+ " if (!entry || !entry.evaluated || !entry.module) {\n" \
+ " @throwTypeError(`require() failed to evaluate module \\\"${resolved}\\\". This is an internal consistentency error.`);\n" \
+ " }\n" \
+ " var exports = Loader.getModuleNamespaceObject(entry.module);\n" \
+ " var commonJS = exports.default;\n" \
+ " if (commonJS && @isObject(commonJS) && Symbol.for(\"CommonJS\") in commonJS) {\n" \
+ " return commonJS();\n" \
+ " }\n" \
+ " return exports;\n" \
"})\n" \
;
diff --git a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h
index f02eec836..31092981d 100644
--- a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h
+++ b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h
@@ -51,17 +51,33 @@ extern const char* const s_jsZigGlobalObjectRequireCode;
extern const int s_jsZigGlobalObjectRequireCodeLength;
extern const JSC::ConstructAbility s_jsZigGlobalObjectRequireCodeConstructAbility;
extern const JSC::ConstructorKind s_jsZigGlobalObjectRequireCodeConstructorKind;
+extern const char* const s_jsZigGlobalObjectLoadModuleCode;
+extern const int s_jsZigGlobalObjectLoadModuleCodeLength;
+extern const JSC::ConstructAbility s_jsZigGlobalObjectLoadModuleCodeConstructAbility;
+extern const JSC::ConstructorKind s_jsZigGlobalObjectLoadModuleCodeConstructorKind;
+extern const char* const s_jsZigGlobalObjectRequireModuleCode;
+extern const int s_jsZigGlobalObjectRequireModuleCodeLength;
+extern const JSC::ConstructAbility s_jsZigGlobalObjectRequireModuleCodeConstructAbility;
+extern const JSC::ConstructorKind s_jsZigGlobalObjectRequireModuleCodeConstructorKind;
#define WEBCORE_FOREACH_JSZIGGLOBALOBJECT_BUILTIN_DATA(macro) \
macro(require, jsZigGlobalObjectRequire, 1) \
+ macro(loadModule, jsZigGlobalObjectLoadModule, 2) \
+ macro(requireModule, jsZigGlobalObjectRequireModule, 2) \
#define WEBCORE_BUILTIN_JSZIGGLOBALOBJECT_REQUIRE 1
+#define WEBCORE_BUILTIN_JSZIGGLOBALOBJECT_LOADMODULE 1
+#define WEBCORE_BUILTIN_JSZIGGLOBALOBJECT_REQUIREMODULE 1
#define WEBCORE_FOREACH_JSZIGGLOBALOBJECT_BUILTIN_CODE(macro) \
macro(jsZigGlobalObjectRequireCode, require, ASCIILiteral(), s_jsZigGlobalObjectRequireCodeLength) \
+ macro(jsZigGlobalObjectLoadModuleCode, loadModule, ASCIILiteral(), s_jsZigGlobalObjectLoadModuleCodeLength) \
+ macro(jsZigGlobalObjectRequireModuleCode, requireModule, ASCIILiteral(), s_jsZigGlobalObjectRequireModuleCodeLength) \
#define WEBCORE_FOREACH_JSZIGGLOBALOBJECT_BUILTIN_FUNCTION_NAME(macro) \
+ macro(loadModule) \
macro(require) \
+ macro(requireModule) \
#define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
JSC::FunctionExecutable* codeName##Generator(JSC::VM&);
diff --git a/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h b/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h
index c52f65d85..fc5e2406a 100644
--- a/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h
+++ b/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h
@@ -1,5 +1,87 @@
//clang-format off
namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
/*
* Copyright (c) 2015 Igalia
* Copyright (c) 2015 Igalia S.L.
diff --git a/src/bun.js/builtins/js/JSZigGlobalObject.js b/src/bun.js/builtins/js/JSZigGlobalObject.js
index cb3446159..7b82067ec 100644
--- a/src/bun.js/builtins/js/JSZigGlobalObject.js
+++ b/src/bun.js/builtins/js/JSZigGlobalObject.js
@@ -28,7 +28,7 @@ function require(name) {
if (typeof name !== "string") {
@throwTypeError("require() expects a string as its argument");
}
-
+
const resolved = this.resolveSync(name, this.path);
var requireCache = (globalThis[Symbol.for("_requireCache")] ||= new @Map);
var cached = requireCache.@get(resolved);
@@ -40,6 +40,7 @@ function require(name) {
return cached;
}
+
// TODO: remove this hardcoding
if (resolved.endsWith(".json")) {
var fs = (globalThis[Symbol.for("_fs")] ||= Bun.fs());
@@ -56,7 +57,133 @@ function require(name) {
var exports = Bun.TOML.parse(fs.readFileSync(resolved, "utf8"));
requireCache.@set(resolved, exports);
return exports;
+ } else {
+ var exports = this.requireModule(this, resolved);
+ requireCache.@set(resolved, exports);
+ return exports;
+ }
+}
+
+function loadModule(meta, resolvedSpecifier) {
+ "use strict";
+ var Loader = globalThis.Loader;
+
+ var queue = @createFIFO();
+ var key = resolvedSpecifier;
+ var registry = Loader.registry;
+ while (key) {
+ @fulfillModuleSync(key);
+ var entry = registry.@get(key);
+
+ // entry.fetch is a Promise<SourceCode>
+ // SourceCode is not a string, it's a JSC::SourceCode object
+ // this pulls it out of the promise without delaying by a tick
+ // the promise is already fullfilled by @fullfillModuleSync
+ var sourceCodeObject = @getPromiseInternalField(
+ entry.fetch,
+ @promiseFieldReactionsOrResult
+ );
+
+ // parseModule() returns a Promise, but the value is already fulfilled
+ // so we just pull it out of the promise here once again
+ // But, this time we do it a little more carefully because this is a JSC function call and not bun source code
+ var moduleRecordPromise = Loader.parseModule(key, sourceCodeObject);
+ var module = entry.module;
+ if (!module && moduleRecordPromise && @isPromise(moduleRecordPromise)) {
+ var reactionsOrResult = @getPromiseInternalField(
+ moduleRecordPromise,
+ @promiseFieldReactionsOrResult
+ );
+ var flags = @getPromiseInternalField(
+ moduleRecordPromise,
+ @promiseFieldFlags
+ );
+ var state = flags & @promiseStateMask;
+
+ // this branch should never happen, but just to be safe
+ if (
+ state === @promiseStatePending ||
+ (reactionsOrResult && @isPromise(reactionsOrResult))
+ ) {
+ @throwTypeError(`require() async module \"${key}\" is unsupported`);
+ } else if (state === @promiseStateRejected) {
+ // this branch happens if there is a syntax error and somehow bun didn't catch it
+ // "throw" is unsupported here, so we use "throwTypeError" (TODO: use SyntaxError but preserve the specifier)
+ @throwTypeError(
+ `${
+ reactionsOrResult?.message ?? "An error occurred"
+ } while parsing module \"${key}\"`
+ );
+ }
+ entry.module = module = reactionsOrResult;
+ } else if (moduleRecordPromise && !module) {
+ entry.module = module = moduleRecordPromise;
+ }
+
+ // This is very similar to "requestInstantiate" in ModuleLoader.js in JavaScriptCore.
+ @setStateToMax(entry, @ModuleLink);
+ var dependenciesMap = module.dependenciesMap;
+ var requestedModules = Loader.requestedModules(module);
+ var dependencies = @newArrayWithSize(requestedModules.length);
+
+ for (var i = 0, length = requestedModules.length; i < length; ++i) {
+ var depName = requestedModules[i];
+
+ // optimization: if it starts with a slash then it's an absolute path
+ // we don't need to run the resolver a 2nd time
+ var depKey =
+ depName[0] === "/"
+ ? depName
+ : Loader.resolveSync(depName, key, @undefined);
+ var depEntry = Loader.ensureRegistered(depKey);
+
+ if (depEntry.state < @ModuleLink) {
+ queue.push(depKey);
+ }
+
+ @putByValDirect(dependencies, i, depEntry);
+ dependenciesMap.@set(depName, depEntry);
+ }
+
+ entry.dependencies = dependencies;
+ key = queue.shift();
+ while (key && (registry.@get(key)?.state ?? @ModuleFetch) >= @ModuleLink) {
+ key = queue.shift();
+ }
+ }
+
+ var linkAndEvaluateResult = Loader.linkAndEvaluateModule(
+ resolvedSpecifier,
+ @undefined
+ );
+ if (linkAndEvaluateResult && @isPromise(linkAndEvaluateResult)) {
+ // if you use top-level await, or any dependencies use top-level await, then we throw here
+ // this means the module will still actually load eventually, but that's okay.
+ @throwTypeError(
+ `require() async module \"${resolvedSpecifier}\" is unsupported`
+ );
+ }
+
+ return Loader.registry.@get(resolvedSpecifier);
+
+}
+
+function requireModule(meta, resolved) {
+ "use strict";
+ var Loader = globalThis.Loader;
+ var entry = Loader.registry.@get(resolved);
+
+ if (!entry || !entry.evaluated) {
+ entry = this.loadModule(meta, resolved);
}
- @throwTypeError(`Dynamic require isn't supported for file type: ${resolved.subsring(resolved.lastIndexOf(".") + 1) || resolved}`);
+ if (!entry || !entry.evaluated || !entry.module) {
+ @throwTypeError(`require() failed to evaluate module \"${resolved}\". This is an internal consistentency error.`);
+ }
+ var exports = Loader.getModuleNamespaceObject(entry.module);
+ var commonJS = exports.default;
+ if (commonJS && @isObject(commonJS) && Symbol.for("CommonJS") in commonJS) {
+ return commonJS();
+ }
+ return exports;
}
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index 937b40cc6..255d50e33 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -641,16 +641,25 @@ pub const VirtualMachine = struct {
const shared_library_suffix = if (Environment.isMac) "dylib" else if (Environment.isLinux) "so" else "";
- inline fn _fetch(
+ const FetchFlags = enum {
+ transpile,
+ print_source,
+ print_source_and_clone,
+
+ pub fn disableTranspiling(this: FetchFlags) bool {
+ return this != .transpile;
+ }
+ };
+ fn _fetch(
+ jsc_vm: *VirtualMachine,
_: *JSGlobalObject,
_specifier: string,
_: string,
log: *logger.Log,
- comptime disable_transpilying: bool,
+ comptime flags: FetchFlags,
) !ResolvedSource {
std.debug.assert(VirtualMachine.vm_loaded);
- var jsc_vm = vm;
-
+ const disable_transpilying = comptime flags.disableTranspiling();
if (jsc_vm.node_modules != null and strings.eqlComptime(_specifier, bun_file_import_path)) {
// We kind of need an abstraction around this.
// Basically we should subclass JSC::SourceCode with:
@@ -707,7 +716,8 @@ pub const VirtualMachine = struct {
jsx.parse = false;
var opts = js_parser.Parser.Options.init(jsx, .js);
opts.enable_bundling = false;
- opts.transform_require_to_import = true;
+ opts.transform_require_to_import = false;
+ opts.features.dynamic_require = true;
opts.can_import_from_bundle = bundler.options.node_modules_bundle != null;
opts.features.hot_module_reloading = false;
opts.features.react_fast_refresh = false;
@@ -1011,7 +1021,11 @@ pub const VirtualMachine = struct {
if (comptime disable_transpilying) {
return ResolvedSource{
.allocator = null,
- .source_code = ZigString.init(parse_result.source.contents),
+ .source_code = switch (comptime flags) {
+ .print_source_and_clone => ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable),
+ .print_source => ZigString.init(parse_result.source.contents),
+ else => unreachable,
+ },
.specifier = ZigString.init(specifier),
.source_url = ZigString.init(path.text),
.hash = 0,
@@ -1340,10 +1354,22 @@ pub const VirtualMachine = struct {
pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void {
var log = logger.Log.init(vm.bundler.allocator);
const spec = specifier.slice();
- const result = _fetch(global, spec, source.slice(), &log, false) catch |err| {
- processFetchLog(global, specifier, source, &log, ret, err);
- return;
- };
+ // threadlocal is cheaper in linux
+ var jsc_vm: *VirtualMachine = if (comptime Environment.isLinux)
+ vm
+ else
+ global.bunVM();
+
+ const result = if (!jsc_vm.bundler.options.disable_transpilation)
+ @call(.{ .modifier = .always_inline }, _fetch, .{ jsc_vm, global, spec, source.slice(), &log, .transpile }) catch |err| {
+ processFetchLog(global, specifier, source, &log, ret, err);
+ return;
+ }
+ else
+ _fetch(jsc_vm, global, spec, source.slice(), &log, .print_source_and_clone) catch |err| {
+ processFetchLog(global, specifier, source, &log, ret, err);
+ return;
+ };
if (log.errors > 0) {
processFetchLog(global, specifier, source, &log, ret, error.LinkError);
@@ -1494,22 +1520,28 @@ pub const VirtualMachine = struct {
}
pub fn loadEntryPoint(this: *VirtualMachine, entry_path: string) !*JSInternalPromise {
- try this.entry_point.generate(@TypeOf(this.bundler), &this.bundler, Fs.PathName.init(entry_path), main_file_name);
this.main = entry_path;
+ try this.entry_point.generate(@TypeOf(this.bundler), &this.bundler, Fs.PathName.init(entry_path), main_file_name);
var promise: *JSInternalPromise = undefined;
- // We first import the node_modules bundle. This prevents any potential TDZ issues.
- // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
- if (this.node_modules != null and !this.has_loaded_node_modules) {
- this.has_loaded_node_modules = true;
- promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(bun_file_import_path)));
- this.waitForPromise(promise);
- if (promise.status(this.global.vm()) == .Rejected)
- return promise;
- }
+ if (!this.bundler.options.disable_transpilation) {
- promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(main_file_name)));
+ // We first import the node_modules bundle. This prevents any potential TDZ issues.
+ // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
+ if (this.node_modules != null and !this.has_loaded_node_modules) {
+ this.has_loaded_node_modules = true;
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(bun_file_import_path)));
+
+ this.waitForPromise(promise);
+ if (promise.status(this.global.vm()) == .Rejected)
+ return promise;
+ }
+
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(main_file_name)));
+ } else {
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(this.main));
+ }
this.waitForPromise(promise);
@@ -1798,7 +1830,7 @@ pub const VirtualMachine = struct {
@maximum(top.position.column_start, 0),
)) |mapping| {
var log = logger.Log.init(default_allocator);
- var original_source = _fetch(this.global, top.source_url.slice(), "", &log, true) catch return;
+ var original_source = _fetch(this, this.global, top.source_url.slice(), "", &log, .print_source) catch return;
const code = original_source.source_code.slice();
top.position.line = mapping.original.lines;
top.position.line_start = mapping.original.lines;
@@ -2688,6 +2720,7 @@ pub const HardcodedModule = enum {
pub const LinkerMap = bun.ComptimeStringMap(
string,
.{
+ .{ "bun", "bun" },
.{ "bun:ffi", "bun:ffi" },
.{ "bun:jsc", "bun:jsc" },
.{ "bun:sqlite", "bun:sqlite" },
diff --git a/src/bun.js/module.exports.js b/src/bun.js/module.exports.js
index f0817a059..6873748e2 100644
--- a/src/bun.js/module.exports.js
+++ b/src/bun.js/module.exports.js
@@ -12,16 +12,6 @@ resolve.paths = () => [];
function require(pathString) {
// this refers to an ImportMeta instance
const resolved = this.resolveSync(pathString);
- if (
- !resolved.endsWith(".node") &&
- !resolved.endsWith(".json") &&
- !resolved.endsWith(".toml")
- ) {
- throw new Error(
- "Dynamic require() in Bun.js currently only supports .node, .json, and .toml files.\n\tConsider using ESM import() instead."
- );
- }
-
return this.require(resolved);
}
@@ -49,13 +39,13 @@ export function createRequire(filename) {
// but we don't support windows yet
process.platform !== "win32" ? "/" : "\\"
);
- var customImportMeta = {
- ...import.meta,
- path: filenameString,
- file:
- lastSlash > -1 ? filenameString.substring(lastSlash + 1) : filenameString,
- dir: lastSlash > -1 ? filenameString.substring(0, lastSlash) : "",
- };
+
+ var customImportMeta = Object.create(import.meta);
+ customImportMeta.path = filenameString;
+ customImportMeta.file =
+ lastSlash > -1 ? filenameString.substring(lastSlash + 1) : filenameString;
+ customImportMeta.dir =
+ lastSlash > -1 ? filenameString.substring(0, lastSlash) : "";
if (isURL) {
customImportMeta.url = filename;