aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/BunPlugin.cpp6
-rw-r--r--src/bun.js/bindings/ModuleLoader.cpp520
-rw-r--r--src/bun.js/bindings/ModuleLoader.h94
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp252
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h21
-rw-r--r--src/bun.js/bindings/bindings.cpp30
-rw-r--r--src/bun.js/bindings/headers-handwritten.h39
-rw-r--r--src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMIsoSubspaces.h1
-rw-r--r--src/bun.js/javascript.zig950
-rw-r--r--src/bun.js/modules/BufferModule.h67
-rw-r--r--src/bun.js/modules/EventsModule.h49
-rw-r--r--src/bundler.zig37
-rw-r--r--src/env.zig1
-rw-r--r--src/fs.zig4
-rw-r--r--src/js_ast.zig9
-rw-r--r--src/js_parser.zig68
-rw-r--r--src/runtime.zig2
-rw-r--r--src/string_immutable.zig37
-rw-r--r--test/bun.js/bun-loader-svelte/bun-loader-svelte.ts18
-rw-r--r--test/bun.js/bun-loader-svelte/package.json4
-rw-r--r--test/bun.js/plugins.test.ts185
22 files changed, 1678 insertions, 717 deletions
diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp
index 0941d2722..ba1d40a0b 100644
--- a/src/bun.js/bindings/BunPlugin.cpp
+++ b/src/bun.js/bindings/BunPlugin.cpp
@@ -303,8 +303,7 @@ extern "C" EncodedJSValue jsFunctionBunPlugin(JSC::JSGlobalObject* globalObject,
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
if (auto* promise = JSC::jsDynamicCast<JSC::JSPromise*>(result)) {
- JSC::throwTypeError(globalObject, throwScope, "setup() does not support promises yet"_s);
- return JSValue::encode(jsUndefined());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(promise));
}
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined()));
@@ -384,8 +383,7 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, ZigStri
if (auto* promise = JSC::jsDynamicCast<JSPromise*>(result)) {
switch (promise->status(vm)) {
case JSPromise::Status::Pending: {
- JSC::throwTypeError(globalObject, throwScope, "onLoad() doesn't support pending promises yet"_s);
- return JSValue::encode({});
+ return JSValue::encode(promise);
}
case JSPromise::Status::Rejected: {
promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(static_cast<unsigned>(JSC::JSPromise::Status::Fulfilled)));
diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp
new file mode 100644
index 000000000..dd5ea01ad
--- /dev/null
+++ b/src/bun.js/bindings/ModuleLoader.cpp
@@ -0,0 +1,520 @@
+#include "root.h"
+#include "headers-handwritten.h"
+
+#include "ModuleLoader.h"
+
+#include "ZigGlobalObject.h"
+#include "JavaScriptCore/JSCInlines.h"
+#include "JavaScriptCore/JSNativeStdFunction.h"
+#include "JavaScriptCore/JSCJSValueInlines.h"
+#include "JavaScriptCore/JSInternalPromise.h"
+#include "JavaScriptCore/JSInternalFieldObjectImpl.h"
+
+#include "ZigSourceProvider.h"
+
+#include "JavaScriptCore/JSSourceCode.h"
+#include "JavaScriptCore/JSString.h"
+#include "JavaScriptCore/JSValueInternal.h"
+#include "JavaScriptCore/JSVirtualMachineInternal.h"
+#include "JavaScriptCore/ObjectConstructor.h"
+#include "JavaScriptCore/OptionsList.h"
+#include "JavaScriptCore/ParserError.h"
+#include "JavaScriptCore/ScriptExecutable.h"
+#include "JavaScriptCore/SourceOrigin.h"
+#include "JavaScriptCore/StackFrame.h"
+#include "JavaScriptCore/StackVisitor.h"
+
+#include "EventEmitter.h"
+#include "JSEventEmitter.h"
+
+#include "../modules/BufferModule.h"
+#include "../modules/EventsModule.h"
+#include "../modules/ProcessModule.h"
+#include "../modules/StringDecoderModule.h"
+#include "../modules/ObjectModule.h"
+#include "../modules/NodeModuleModule.h"
+
+namespace Bun {
+using namespace Zig;
+using namespace WebCore;
+
+static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
+{
+ JSC::VM& vm = globalObject->vm();
+ JSInternalPromise* promise = JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
+ promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
+ promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Rejected)));
+ return promise;
+}
+
+static JSC::JSInternalPromise* resolvedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
+{
+ JSC::VM& vm = globalObject->vm();
+
+ JSInternalPromise* promise = JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
+ promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
+ promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Fulfilled)));
+ return promise;
+}
+
+using namespace JSC;
+
+static OnLoadResult handleOnLoadObjectResult(Zig::GlobalObject* globalObject, JSC::JSObject* object)
+{
+ OnLoadResult result {};
+ result.type = OnLoadResultTypeObject;
+ JSC::VM& vm = globalObject->vm();
+ if (JSC::JSValue exportsValue = object->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "exports"_s))) {
+ if (exportsValue.isObject()) {
+ result.value.object = exportsValue;
+ return result;
+ }
+ }
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, createTypeError(globalObject, "\"object\" loader must return an \"exports\" object"_s));
+ result.type = OnLoadResultTypeError;
+ result.value.error = scope.exception();
+ scope.clearException();
+ scope.release();
+ return result;
+}
+
+JSC::JSInternalPromise* PendingVirtualModuleResult::internalPromise()
+{
+ return jsCast<JSC::JSInternalPromise*>(internalField(2).get());
+}
+
+const ClassInfo PendingVirtualModuleResult::s_info = { "PendingVirtualModule"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(PendingVirtualModuleResult) };
+
+PendingVirtualModuleResult* PendingVirtualModuleResult::create(VM& vm, Structure* structure)
+{
+ PendingVirtualModuleResult* mod = new (NotNull, allocateCell<PendingVirtualModuleResult>(vm)) PendingVirtualModuleResult(vm, structure);
+ return mod;
+}
+Structure* PendingVirtualModuleResult::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+ return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
+}
+
+PendingVirtualModuleResult::PendingVirtualModuleResult(VM& vm, Structure* structure)
+ : Base(vm, structure)
+{
+}
+
+void PendingVirtualModuleResult::finishCreation(VM& vm, const WTF::String& specifier, const WTF::String& referrer)
+{
+ Base::finishCreation(vm);
+ Base::internalField(0).set(vm, this, JSC::jsString(vm, specifier));
+ Base::internalField(1).set(vm, this, JSC::jsString(vm, referrer));
+ Base::internalField(2).set(vm, this, JSC::JSInternalPromise::create(vm, globalObject()->internalPromiseStructure()));
+}
+
+template<typename Visitor>
+void PendingVirtualModuleResult::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ auto* thisObject = jsCast<PendingVirtualModuleResult*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+}
+
+DEFINE_VISIT_CHILDREN(PendingVirtualModuleResult);
+
+PendingVirtualModuleResult* PendingVirtualModuleResult::create(JSC::JSGlobalObject* globalObject, const WTF::String& specifier, const WTF::String& referrer)
+{
+ auto* virtualModule = create(globalObject->vm(), reinterpret_cast<Zig::GlobalObject*>(globalObject)->pendingVirtualModuleResultStructure());
+ virtualModule->finishCreation(globalObject->vm(), specifier, referrer);
+ return virtualModule;
+}
+
+OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue)
+{
+ OnLoadResult result = {};
+ result.type = OnLoadResultTypeError;
+ JSC::VM& vm = globalObject->vm();
+ result.value.error = JSC::jsUndefined();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ BunLoaderType loader = BunLoaderTypeNone;
+
+ JSC::JSObject* object = objectValue.getObject();
+ if (UNLIKELY(!object)) {
+ scope.throwException(globalObject, JSC::createError(globalObject, "Expected onLoad callback to return an object"_s));
+ result.value.error = scope.exception();
+ scope.clearException();
+ scope.release();
+ return result;
+ }
+
+ if (JSC::JSValue loaderValue = object->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "loader"_s))) {
+ if (loaderValue.isString()) {
+ if (JSC::JSString* loaderJSString = loaderValue.toStringOrNull(globalObject)) {
+ WTF::String loaderString = loaderJSString->value(globalObject);
+ if (loaderString == "js"_s) {
+ loader = BunLoaderTypeJS;
+ } else if (loaderString == "object"_s) {
+ return handleOnLoadObjectResult(globalObject, object);
+ } else if (loaderString == "jsx"_s) {
+ loader = BunLoaderTypeJSX;
+ } else if (loaderString == "ts"_s) {
+ loader = BunLoaderTypeTS;
+ } else if (loaderString == "tsx"_s) {
+ loader = BunLoaderTypeTSX;
+ } else if (loaderString == "json"_s) {
+ loader = BunLoaderTypeJSON;
+ } else if (loaderString == "toml"_s) {
+ loader = BunLoaderTypeTOML;
+ }
+ }
+ }
+ }
+
+ if (UNLIKELY(loader == BunLoaderTypeNone)) {
+ throwException(globalObject, scope, createError(globalObject, "Expected loader to be one of \"js\", \"jsx\", \"object\", \"ts\", \"tsx\", \"toml\", or \"json\""_s));
+ result.value.error = scope.exception();
+ scope.clearException();
+ scope.release();
+ return result;
+ }
+
+ result.value.sourceText.loader = loader;
+ result.value.sourceText.value = JSValue {};
+ result.value.sourceText.string = {};
+
+ if (JSC::JSValue contentsValue = object->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "contents"_s))) {
+ if (contentsValue.isString()) {
+ if (JSC::JSString* contentsJSString = contentsValue.toStringOrNull(globalObject)) {
+ result.value.sourceText.string = Zig::toZigString(contentsJSString, globalObject);
+ result.value.sourceText.value = contentsValue;
+ }
+ } else if (JSC::JSArrayBufferView* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(contentsValue)) {
+ result.value.sourceText.string = ZigString { reinterpret_cast<const unsigned char*>(view->vector()), view->byteLength() };
+ result.value.sourceText.value = contentsValue;
+ }
+ }
+
+ if (UNLIKELY(result.value.sourceText.value.isEmpty())) {
+ throwException(globalObject, scope, createError(globalObject, "Expected \"contents\" to be a string or an ArrayBufferView"_s));
+ result.value.error = scope.exception();
+ scope.clearException();
+ scope.release();
+ return result;
+ }
+
+ result.type = OnLoadResultTypeCode;
+ return result;
+}
+
+static OnLoadResult handleOnLoadResult(Zig::GlobalObject* globalObject, JSC::JSValue objectValue)
+{
+ if (JSC::JSPromise* promise = JSC::jsDynamicCast<JSC::JSPromise*>(objectValue)) {
+ OnLoadResult result = {};
+ result.type = OnLoadResultTypePromise;
+ result.value.promise = objectValue;
+ return result;
+ }
+
+ return handleOnLoadResultNotPromise(globalObject, objectValue);
+}
+
+template<bool allowPromise>
+static JSValue handleVirtualModuleResult(
+ Zig::GlobalObject* globalObject,
+ JSValue virtualModuleResult,
+ ErrorableResolvedSource* res,
+ ZigString* specifier,
+ ZigString* referrer)
+{
+ auto onLoadResult = handleOnLoadResult(globalObject, virtualModuleResult);
+ JSC::VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto reject = [&](JSC::JSValue exception) -> JSValue {
+ if constexpr (allowPromise) {
+ return rejectedInternalPromise(globalObject, exception);
+ } else {
+ throwException(globalObject, scope, exception);
+ return exception;
+ }
+ };
+
+ auto resolve = [&](JSValue code) -> JSValue {
+ res->success = true;
+ if constexpr (allowPromise) {
+ return resolvedInternalPromise(globalObject, code);
+ } else {
+ return code;
+ }
+ };
+
+ auto rejectOrResolve = [&](JSValue code) -> JSValue {
+ if (auto* exception = scope.exception()) {
+ if constexpr (allowPromise) {
+ scope.clearException();
+ return rejectedInternalPromise(globalObject, exception);
+ } else {
+ return exception;
+ }
+ }
+
+ res->success = true;
+
+ if constexpr (allowPromise) {
+ return resolvedInternalPromise(globalObject, code);
+ } else {
+ return code;
+ }
+ };
+
+ switch (onLoadResult.type) {
+ case OnLoadResultTypeCode: {
+ Bun__transpileVirtualModule(globalObject, specifier, referrer, &onLoadResult.value.sourceText.string, onLoadResult.value.sourceText.loader, res);
+ if (!res->success) {
+ return reject(JSValue::decode(reinterpret_cast<EncodedJSValue>(res->result.err.ptr)));
+ }
+
+ auto provider = Zig::SourceProvider::create(res->result.value);
+ return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
+ }
+ case OnLoadResultTypeError: {
+ return reject(onLoadResult.value.error);
+ }
+
+ case OnLoadResultTypeObject: {
+ JSC::JSObject* object = onLoadResult.value.object.getObject();
+ JSC::ensureStillAliveHere(object);
+ auto function = generateObjectModuleSourceCode(
+ globalObject,
+ object);
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(WTFMove(function),
+ JSC::SourceOrigin(), Zig::toString(*specifier)));
+ JSC::ensureStillAliveHere(object);
+ return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source)));
+ }
+
+ case OnLoadResultTypePromise: {
+ JSC::JSPromise* promise = jsCast<JSC::JSPromise*>(onLoadResult.value.promise);
+ JSFunction* performPromiseThenFunction = globalObject->performPromiseThenFunction();
+ auto callData = JSC::getCallData(performPromiseThenFunction);
+ ASSERT(callData.type != CallData::Type::None);
+ auto specifierString = Zig::toString(*specifier);
+ auto referrerString = Zig::toString(*referrer);
+ PendingVirtualModuleResult* pendingModule = PendingVirtualModuleResult::create(globalObject, specifierString, referrerString);
+ JSC::JSInternalPromise* internalPromise = pendingModule->internalPromise();
+ MarkedArgumentBuffer arguments;
+ arguments.append(promise);
+ arguments.append(globalObject->thenable(jsFunctionOnLoadObjectResultResolve));
+ arguments.append(globalObject->thenable(jsFunctionOnLoadObjectResultReject));
+ arguments.append(jsUndefined());
+ arguments.append(pendingModule);
+ ASSERT(!arguments.hasOverflowed());
+ JSC::call(globalObject, performPromiseThenFunction, callData, jsUndefined(), arguments);
+ return internalPromise;
+ }
+ default: {
+ __builtin_unreachable();
+ }
+ }
+}
+
+template<bool allowPromise>
+static JSValue fetchSourceCode(
+ Zig::GlobalObject* globalObject,
+ ErrorableResolvedSource* res,
+ ZigString* specifier,
+ ZigString* referrer)
+{
+ void* bunVM = globalObject->bunVM();
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto reject = [&](JSC::JSValue exception) -> JSValue {
+ if constexpr (allowPromise) {
+ return rejectedInternalPromise(globalObject, exception);
+ } else {
+ throwException(globalObject, scope, exception);
+ return JSC::jsUndefined();
+ }
+ };
+
+ auto resolve = [&](JSValue code) -> JSValue {
+ if constexpr (allowPromise) {
+ return resolvedInternalPromise(globalObject, code);
+ } else {
+ return code;
+ }
+ };
+
+ auto rejectOrResolve = [&](JSValue code) -> JSValue {
+ if (auto* exception = scope.exception()) {
+ scope.clearException();
+ return rejectedInternalPromise(globalObject, exception);
+ }
+
+ if constexpr (allowPromise) {
+ return resolvedInternalPromise(globalObject, code);
+ } else {
+ return code;
+ }
+ };
+
+ if (Bun__fetchBuiltinModule(bunVM, globalObject, specifier, referrer, res)) {
+ if (!res->success) {
+ throwException(scope, res->result.err, globalObject);
+ auto* exception = scope.exception();
+ scope.clearException();
+ return reject(exception);
+ }
+
+ auto moduleKey = Zig::toString(*specifier);
+
+ switch (res->result.value.tag) {
+ case SyntheticModuleType::Module: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateNodeModuleModule,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));
+ }
+
+ case SyntheticModuleType::Buffer: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateBufferSourceCode,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
+
+ return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));
+ }
+ case SyntheticModuleType::Process: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateProcessSourceCode,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));
+ }
+ case SyntheticModuleType::Events: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateEventsSourceCode,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));
+ }
+ case SyntheticModuleType::StringDecoder: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateStringDecoderSourceCode,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));
+ }
+ default: {
+ auto provider = Zig::SourceProvider::create(res->result.value);
+ return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
+ }
+ }
+ }
+
+ if (JSC::JSValue virtualModuleResult = JSValue::decode(Bun__runVirtualModule(globalObject, specifier))) {
+ return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer);
+ }
+
+ Bun__transpileFile(bunVM, globalObject, specifier, referrer, res);
+ if (!res->success) {
+ throwException(scope, res->result.err, globalObject);
+ auto* exception = scope.exception();
+ scope.clearException();
+ return reject(exception);
+ }
+
+ auto provider = Zig::SourceProvider::create(res->result.value);
+ return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
+}
+
+extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)
+{
+ JSC::VM& vm = globalObject->vm();
+ ErrorableResolvedSource res = {};
+ res.success = false;
+ JSC::JSValue objectResult = callFrame->argument(0);
+ PendingVirtualModuleResult* pendingModule = JSC::jsCast<PendingVirtualModuleResult*>(callFrame->argument(1));
+ JSC::JSValue specifierString = pendingModule->internalField(0).get();
+ JSC::JSValue referrerString = pendingModule->internalField(1).get();
+ pendingModule->internalField(0).set(vm, pendingModule, JSC::jsUndefined());
+ pendingModule->internalField(1).set(vm, pendingModule, JSC::jsUndefined());
+ JSC::JSInternalPromise* promise = pendingModule->internalPromise();
+
+ ZigString specifier = Zig::toZigString(specifierString, globalObject);
+ ZigString referrer = Zig::toZigString(referrerString, globalObject);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSC::JSValue result = handleVirtualModuleResult<false>(reinterpret_cast<Zig::GlobalObject*>(globalObject), objectResult, &res, &specifier, &referrer);
+ if (res.success) {
+ if (scope.exception()) {
+ auto retValue = JSValue::encode(promise->rejectWithCaughtException(globalObject, scope));
+ pendingModule->internalField(2).set(vm, pendingModule, JSC::jsUndefined());
+ return retValue;
+ }
+ scope.release();
+ promise->resolve(globalObject, result);
+ pendingModule->internalField(2).set(vm, pendingModule, JSC::jsUndefined());
+ } else {
+ throwException(globalObject, scope, result);
+ auto retValue = JSValue::encode(promise->rejectWithCaughtException(globalObject, scope));
+ pendingModule->internalField(2).set(vm, pendingModule, JSC::jsUndefined());
+ return retValue;
+ }
+ return JSValue::encode(jsUndefined());
+}
+
+extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultReject(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)
+{
+ JSC::VM& vm = globalObject->vm();
+ ErrorableResolvedSource res = {};
+ JSC::JSValue reason = callFrame->argument(0);
+ PendingVirtualModuleResult* pendingModule = JSC::jsCast<PendingVirtualModuleResult*>(callFrame->argument(1));
+ JSC::JSValue specifierString = pendingModule->internalField(0).get();
+ JSC::JSValue referrerString = pendingModule->internalField(1).get();
+ pendingModule->internalField(0).set(vm, pendingModule, JSC::jsUndefined());
+ pendingModule->internalField(1).set(vm, pendingModule, JSC::jsUndefined());
+ JSC::JSInternalPromise* promise = pendingModule->internalPromise();
+
+ ZigString specifier = Zig::toZigString(specifierString, globalObject);
+ ZigString referrer = Zig::toZigString(referrerString, globalObject);
+ pendingModule->internalField(2).set(vm, pendingModule, JSC::jsUndefined());
+ promise->reject(globalObject, reason);
+
+ return JSValue::encode(reason);
+}
+
+JSValue fetchSourceCodeSync(
+ Zig::GlobalObject* globalObject,
+ ErrorableResolvedSource* res,
+ ZigString* specifier,
+ ZigString* referrer)
+{
+ return fetchSourceCode<false>(globalObject, res, specifier, referrer);
+}
+
+JSValue fetchSourceCodeAsync(
+ Zig::GlobalObject* globalObject,
+ ErrorableResolvedSource* res,
+ ZigString* specifier,
+ ZigString* referrer)
+{
+ return fetchSourceCode<true>(globalObject, res, specifier, referrer);
+}
+}
+namespace JSC {
+
+template<unsigned passedNumberOfInternalFields>
+template<typename Visitor>
+void JSInternalFieldObjectImpl<passedNumberOfInternalFields>::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ auto* thisObject = jsCast<JSInternalFieldObjectImpl*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+ visitor.appendValues(thisObject->m_internalFields, numberOfInternalFields);
+}
+
+DEFINE_VISIT_CHILDREN_WITH_MODIFIER(template<unsigned passedNumberOfInternalFields>, JSInternalFieldObjectImpl<passedNumberOfInternalFields>);
+
+} // namespace JSC
diff --git a/src/bun.js/bindings/ModuleLoader.h b/src/bun.js/bindings/ModuleLoader.h
new file mode 100644
index 000000000..98f8b7dbb
--- /dev/null
+++ b/src/bun.js/bindings/ModuleLoader.h
@@ -0,0 +1,94 @@
+#include "root.h"
+#include "headers-handwritten.h"
+
+#include "JavaScriptCore/JSCInlines.h"
+#include "BunClientData.h"
+
+namespace Zig {
+class GlobalObject;
+}
+
+namespace JSC {
+class JSInternalPromise;
+}
+
+namespace Bun {
+using namespace JSC;
+
+typedef uint8_t OnLoadResultType;
+const OnLoadResultType OnLoadResultTypeError = 0;
+const OnLoadResultType OnLoadResultTypeCode = 1;
+const OnLoadResultType OnLoadResultTypeObject = 2;
+const OnLoadResultType OnLoadResultTypePromise = 3;
+
+struct CodeString {
+ ZigString string;
+ JSC::JSValue value;
+ BunLoaderType loader;
+};
+
+union OnLoadResultValue {
+ CodeString sourceText;
+ JSC::JSValue object;
+ JSC::JSValue promise;
+ JSC::JSValue error;
+};
+
+struct OnLoadResult {
+ OnLoadResultValue value;
+ OnLoadResultType type;
+};
+
+class PendingVirtualModuleResult : public JSC::JSInternalFieldObjectImpl<3> {
+public:
+ using Base = JSC::JSInternalFieldObjectImpl<3>;
+
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<PendingVirtualModuleResult, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForPendingVirtualModuleResult.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForPendingVirtualModuleResult = WTFMove(space); },
+ [](auto& spaces) { return spaces.m_subspaceForPendingVirtualModuleResult.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForPendingVirtualModuleResult = WTFMove(space); });
+ }
+
+ JS_EXPORT_PRIVATE static PendingVirtualModuleResult* create(VM&, Structure*);
+ static PendingVirtualModuleResult* create(JSC::JSGlobalObject* globalObject, const WTF::String& specifier, const WTF::String& referrer);
+ static PendingVirtualModuleResult* createWithInitialValues(VM&, Structure*);
+ static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
+
+ JSC::JSInternalPromise* internalPromise();
+
+ static std::array<JSValue, numberOfInternalFields> initialValues()
+ {
+ return { {
+ jsUndefined(),
+ jsUndefined(),
+ jsUndefined(),
+ } };
+ }
+
+ DECLARE_EXPORT_INFO;
+ DECLARE_VISIT_CHILDREN;
+
+ PendingVirtualModuleResult(JSC::VM&, JSC::Structure*);
+ void finishCreation(JSC::VM&, const WTF::String& specifier, const WTF::String& referrer);
+};
+
+OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue);
+JSValue fetchSourceCodeSync(
+ Zig::GlobalObject* globalObject,
+ ErrorableResolvedSource* res,
+ ZigString* specifier,
+ ZigString* referrer);
+
+JSValue fetchSourceCodeAsync(
+ Zig::GlobalObject* globalObject,
+ ErrorableResolvedSource* res,
+ ZigString* specifier,
+ ZigString* referrer);
+
+} // namespace Bun \ No newline at end of file
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 888bf8f6b..2753707e8 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -103,6 +103,7 @@
#include "JSSQLStatement.h"
#include "ReadableStreamBuiltins.h"
#include "BunJSCModule.h"
+#include "ModuleLoader.h"
#include "ZigGeneratedClasses.h"
@@ -160,13 +161,6 @@ using JSBuffer = WebCore::JSBuffer;
#include "DOMJITHelpers.h"
#include <JavaScriptCore/DFGAbstractHeap.h>
-#include "../modules/BufferModule.h"
-#include "../modules/EventsModule.h"
-#include "../modules/ProcessModule.h"
-#include "../modules/StringDecoderModule.h"
-#include "../modules/ObjectModule.h"
-#include "../modules/NodeModuleModule.h"
-
// #include <iostream>
static bool has_loaded_jsc = false;
@@ -1022,6 +1016,7 @@ JSC:
static NeverDestroyed<const String> bunJSCString(MAKE_STATIC_STRING_IMPL("bun:jsc"));
static NeverDestroyed<const String> bunStreamString(MAKE_STATIC_STRING_IMPL("bun:stream"));
static NeverDestroyed<const String> noopString(MAKE_STATIC_STRING_IMPL("noop"));
+ static NeverDestroyed<const String> createImportMeta(MAKE_STATIC_STRING_IMPL("createImportMeta"));
JSC::JSValue moduleName = callFrame->argument(0);
if (moduleName.isNumber()) {
@@ -1081,6 +1076,11 @@ JSC:
return JSValue::encode(obj);
}
+ if (string == createImportMeta) {
+ Zig::ImportMetaObject* obj = Zig::ImportMetaObject::create(globalObject, callFrame->argument(1));
+ return JSValue::encode(obj);
+ }
+
if (UNLIKELY(string == noopString)) {
auto* obj = constructEmptyObject(globalObject);
obj->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getterSetter"_s)), JSC::CustomGetterSetter::create(vm, noop_getter, noop_setter), 0);
@@ -1884,6 +1884,11 @@ void GlobalObject::finishCreation(VM& vm)
init.set(JSModuleNamespaceObject::createStructure(init.vm, init.owner, init.owner->objectPrototype()));
});
+ this->m_pendingVirtualModuleResultStructure.initLater(
+ [](const Initializer<Structure>& init) {
+ init.set(Bun::PendingVirtualModuleResult::createStructure(init.vm, init.owner, init.owner->objectPrototype()));
+ });
+
this->initGeneratedLazyClasses();
m_NapiClassStructure.initLater(
@@ -2655,87 +2660,27 @@ static JSC_DEFINE_HOST_FUNCTION(functionFulfillModuleSync,
res.result.err.code = 0;
res.result.err.ptr = nullptr;
- Zig__GlobalObject__fetch(&res, globalObject, &specifier, &specifier);
+ JSValue result = Bun::fetchSourceCodeSync(
+ reinterpret_cast<Zig::GlobalObject*>(globalObject),
+ &res,
+ &specifier,
+ &specifier);
- if (!res.success) {
- throwException(scope, res.result.err, globalObject);
- return JSValue::encode(JSC::jsUndefined());
+ if (result.isUndefined() || !result) {
+ return JSValue::encode(result);
}
- switch (res.result.value.tag) {
- case SyntheticModuleType::Buffer: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(
- generateBufferSourceCode,
- JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:buffer"_s)), WTFMove(moduleKey)));
-
- globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
- }
- case SyntheticModuleType::ObjectModule: {
- JSC::EncodedJSValue encodedValue = reinterpret_cast<JSC::EncodedJSValue>(
- bitwise_cast<int64_t>(reinterpret_cast<size_t>(res.result.value.source_code.ptr)));
- JSC::JSObject* object = JSC::JSValue::decode(encodedValue).getObject();
- auto function = generateObjectModuleSourceCode(
- globalObject,
- object);
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(WTFMove(function),
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
- }
- case SyntheticModuleType::Process: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(
- generateProcessSourceCode,
- JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:process"_s)), WTFMove(moduleKey)));
-
- globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
- }
- case SyntheticModuleType::Events: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(
- generateEventsSourceCode,
- JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:events"_s)), WTFMove(moduleKey)));
-
- globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
- }
- case SyntheticModuleType::Module: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(
- generateNodeModuleModule,
- JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:module"_s)), WTFMove(moduleKey)));
-
- globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
- }
- case SyntheticModuleType::StringDecoder: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(
- generateStringDecoderSourceCode,
- JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:string_decoder"_s)), WTFMove(moduleKey)));
-
- globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
- }
- default: {
- 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()));
- }
- }
+ globalObject->moduleLoader()->provideFetch(globalObject, key, jsCast<JSC::JSSourceCode*>(result)->sourceCode());
+ RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
+}
+
+static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
+{
+ JSC::VM& vm = globalObject->vm();
+ JSInternalPromise* promise = JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
+ promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
+ promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Rejected)));
+ return promise;
}
JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject,
@@ -2743,20 +2688,15 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb
JSValue value1, JSValue value2)
{
JSC::VM& vm = globalObject->vm();
- JSC::JSInternalPromise* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
auto scope = DECLARE_THROW_SCOPE(vm);
- auto rejectWithError = [&](JSC::JSValue error) {
- promise->reject(globalObject, error);
- return promise;
- };
-
auto moduleKey = key.toWTFString(globalObject);
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
+ if (UNLIKELY(scope.exception()))
+ return rejectedInternalPromise(globalObject, scope.exception()->value());
if (moduleKey.endsWith(".node"_s)) {
- return rejectWithError(createTypeError(globalObject, "To load Node-API modules, use require() or process.dlopen instead of import."_s));
+ return rejectedInternalPromise(globalObject, createTypeError(globalObject, "To load Node-API modules, use require() or process.dlopen instead of import."_s));
}
auto moduleKeyZig = toZigString(moduleKey);
@@ -2766,123 +2706,19 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb
res.result.err.code = 0;
res.result.err.ptr = nullptr;
- Zig__GlobalObject__fetch(&res, globalObject, &moduleKeyZig, &source);
-
- if (!res.success) {
- throwException(scope, res.result.err, globalObject);
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
- }
-
- switch (res.result.value.tag) {
- case 1: {
- auto buffer = Vector<uint8_t>(res.result.value.source_code.ptr, res.result.value.source_code.len);
- auto source = JSC::SourceCode(
- JSC::WebAssemblySourceProvider::create(WTFMove(buffer),
- JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(Zig::toString(res.result.value.source_url))),
- WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
-
- promise->resolve(globalObject, sourceCode);
- scope.release();
-
- globalObject->vm().drainMicrotasks();
- return promise;
- }
- case SyntheticModuleType::ObjectModule: {
- JSC::EncodedJSValue encodedValue = reinterpret_cast<JSC::EncodedJSValue>(
- bitwise_cast<int64_t>(reinterpret_cast<size_t>(res.result.value.source_code.ptr)));
- JSC::JSObject* object = JSC::JSValue::decode(encodedValue).getObject();
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(generateObjectModuleSourceCode(
- globalObject,
- object),
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
+ JSValue result = Bun::fetchSourceCodeAsync(
+ reinterpret_cast<Zig::GlobalObject*>(globalObject),
+ &res,
+ &moduleKeyZig,
+ &source);
- promise->resolve(globalObject, sourceCode);
- scope.release();
- return promise;
- }
- case SyntheticModuleType::Module: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(generateNodeModuleModule,
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
-
- promise->resolve(globalObject, sourceCode);
- scope.release();
- return promise;
- }
-
- case SyntheticModuleType::Buffer: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(generateBufferSourceCode,
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
-
- promise->resolve(globalObject, sourceCode);
- scope.release();
- return promise;
- }
- case SyntheticModuleType::Process: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(generateProcessSourceCode,
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
-
- promise->resolve(globalObject, sourceCode);
- scope.release();
- return promise;
- }
- case SyntheticModuleType::Events: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(generateEventsSourceCode,
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
-
- promise->resolve(globalObject, sourceCode);
- scope.release();
- return promise;
- }
- case SyntheticModuleType::StringDecoder: {
- auto source = JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(generateStringDecoderSourceCode,
- JSC::SourceOrigin(), WTFMove(moduleKey)));
-
- auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
- RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
-
- promise->resolve(globalObject, sourceCode);
- scope.release();
- return promise;
- }
- default: {
- auto provider = Zig::SourceProvider::create(res.result.value);
- auto jsSourceCode = JSC::JSSourceCode::create(vm, JSC::SourceCode(provider));
- promise->resolve(globalObject, jsSourceCode);
- }
+ if (auto* internalPromise = JSC::jsDynamicCast<JSC::JSInternalPromise*>(result)) {
+ return internalPromise;
+ } else if (auto* promise = JSC::jsDynamicCast<JSC::JSPromise*>(result)) {
+ return jsCast<JSC::JSInternalPromise*>(promise);
+ } else {
+ return rejectedInternalPromise(globalObject, result);
}
-
- // if (provider.ptr()->isBytecodeCacheEnabled()) {
- // provider.ptr()->readOrGenerateByteCodeCache(vm, jsSourceCode->sourceCode());
- // }
-
- scope.release();
-
- globalObject->vm().drainMicrotasks();
- return promise;
}
JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObject* globalObject,
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 6c5c23b3a..160aef714 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -35,7 +35,9 @@ class EventLoopTask;
#include "BunPlugin.h"
extern "C" void Bun__reportError(JSC__JSGlobalObject*, JSC__JSValue);
-
+// defined in ModuleLoader.cpp
+extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame);
+extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultReject(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame);
// #include "EventTarget.h"
// namespace WebCore {
@@ -239,7 +241,10 @@ public:
Bun__HTTPRequestContextDebugTLS__onResolve,
Bun__HTTPRequestContextDebugTLS__onResolveStream,
+ jsFunctionOnLoadObjectResultResolve,
+ jsFunctionOnLoadObjectResultReject,
};
+ static constexpr size_t promiseFunctionsSize = 18;
static PromiseFunctions promiseHandlerID(EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1))
{
@@ -275,6 +280,14 @@ public:
return PromiseFunctions::Bun__HTTPRequestContextDebugTLS__onResolve;
} else if (handler == Bun__HTTPRequestContextDebugTLS__onResolveStream) {
return PromiseFunctions::Bun__HTTPRequestContextDebugTLS__onResolveStream;
+ } else if (handler == Bun__HTTPRequestContextDebugTLS__onResolveStream) {
+ return PromiseFunctions::Bun__HTTPRequestContextDebugTLS__onResolveStream;
+ } else if (handler == Bun__HTTPRequestContextDebugTLS__onResolveStream) {
+ return PromiseFunctions::Bun__HTTPRequestContextDebugTLS__onResolveStream;
+ } else if (handler == jsFunctionOnLoadObjectResultResolve) {
+ return PromiseFunctions::jsFunctionOnLoadObjectResultResolve;
+ } else if (handler == jsFunctionOnLoadObjectResultReject) {
+ return PromiseFunctions::jsFunctionOnLoadObjectResultReject;
} else {
RELEASE_ASSERT_NOT_REACHED();
}
@@ -300,7 +313,7 @@ public:
mutable WriteBarrier<JSFunction> m_readableStreamToJSON;
mutable WriteBarrier<JSFunction> m_readableStreamToArrayBuffer;
mutable WriteBarrier<JSFunction> m_assignToStream;
- mutable WriteBarrier<JSFunction> m_thenables[16];
+ mutable WriteBarrier<JSFunction> m_thenables[promiseFunctionsSize + 1];
void trackFFIFunction(JSC::JSFunction* function)
{
@@ -311,6 +324,8 @@ public:
BunPlugin::OnResolve onResolvePlugins[BunPluginTargetMax + 1] {};
BunPluginTarget defaultBunPluginTarget = BunPluginTargetBun;
+ JSC::Structure* pendingVirtualModuleResultStructure() { return m_pendingVirtualModuleResultStructure.get(this); }
+
// When a napi module initializes on dlopen, we need to know what the value is
JSValue pendingNapiModule = JSValue {};
@@ -344,6 +359,8 @@ private:
LazyProperty<JSGlobalObject, JSMap> m_requireMap;
LazyProperty<JSGlobalObject, JSObject> m_performanceObject;
+ LazyProperty<JSGlobalObject, JSC::Structure> m_pendingVirtualModuleResultStructure;
+
LazyProperty<JSGlobalObject, JSObject> m_encodeIntoObjectPrototype;
// LazyProperty<JSGlobalObject, WebCore::JSEventTarget> m_eventTarget;
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 81e9c602b..bb0718ea9 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -873,27 +873,33 @@ bool JSC__JSModuleLoader__checkSyntax(JSC__JSGlobalObject* arg0, const JSC__Sour
return result;
}
-JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* arg0, const unsigned char* arg1,
+JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* globalObject, const unsigned char* arg1,
size_t arg2, const unsigned char* arg3, size_t arg4,
JSC__JSValue JSValue5, JSC__JSValue* arg6)
{
- WTF::String src = WTF::String(WTF::StringImpl::createWithoutCopying(arg1, arg2));
- WTF::URL origin = WTF::URL::fileURLWithFileSystemPath(WTF::StringView(arg3, arg4));
+ WTF::String src = WTF::String::fromUTF8(arg1, arg2).isolatedCopy();
+ WTF::URL origin = WTF::URL::fileURLWithFileSystemPath(WTF::String(WTF::StringImpl::createWithoutCopying(arg3, arg4))).isolatedCopy();
- JSC::VM& vm = arg0->vm();
- JSC::JSLockHolder locker(vm);
+ JSC::VM& vm = globalObject->vm();
JSC::SourceCode sourceCode = JSC::makeSource(
- src, JSC::SourceOrigin { origin }, origin.lastPathComponent().toStringWithoutCopying(),
+ src, JSC::SourceOrigin { origin }, origin.fileSystemPath(),
WTF::TextPosition(), JSC::SourceProviderSourceType::Module);
- WTF::NakedPtr<JSC::Exception> exception;
- auto val = JSC::evaluate(arg0, sourceCode, JSC::JSValue(), exception);
- if (exception.get()) {
- *arg6 = JSC::JSValue::encode(JSC::JSValue(exception.get()));
+ globalObject->moduleLoader()->provideFetch(globalObject, jsString(vm, origin.fileSystemPath()), WTFMove(sourceCode));
+ auto* promise = JSC::importModule(globalObject, JSC::Identifier::fromString(vm, origin.fileSystemPath()), JSValue(), JSValue());
+
+ if (promise->status(vm) == JSC::JSPromise::Status::Pending) {
+ vm.drainMicrotasks();
}
- vm.drainMicrotasks();
- return JSC::JSValue::encode(val);
+ if (promise->status(vm) == JSC::JSPromise::Status::Fulfilled) {
+ return JSC::JSValue::encode(promise->result(vm));
+ } else if (promise->status(vm) == JSC::JSPromise::Status::Rejected) {
+ *arg6 = JSC::JSValue::encode(promise->result(vm));
+ return JSC::JSValue::encode(JSC::jsUndefined());
+ } else {
+ return JSC::JSValue::encode(promise);
+ }
}
JSC__JSInternalPromise* JSC__JSModuleLoader__importModule(JSC__JSGlobalObject* arg0,
const JSC__Identifier* arg1)
diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h
index 45d4752e0..36678e22e 100644
--- a/src/bun.js/bindings/headers-handwritten.h
+++ b/src/bun.js/bindings/headers-handwritten.h
@@ -116,6 +116,19 @@ const JSErrorCode JSErrorCodeOutOfMemoryError = 8;
const JSErrorCode JSErrorCodeStackOverflow = 253;
const JSErrorCode JSErrorCodeUserErrorCode = 254;
+typedef uint8_t BunLoaderType;
+const BunLoaderType BunLoaderTypeNone = 0;
+const BunLoaderType BunLoaderTypeJSX = 1;
+const BunLoaderType BunLoaderTypeJS = 2;
+const BunLoaderType BunLoaderTypeTS = 3;
+const BunLoaderType BunLoaderTypeTSX = 4;
+const BunLoaderType BunLoaderTypeCSS = 5;
+const BunLoaderType BunLoaderTypeFILE = 6;
+const BunLoaderType BunLoaderTypeJSON = 7;
+const BunLoaderType BunLoaderTypeTOML = 8;
+const BunLoaderType BunLoaderTypeWASM = 9;
+const BunLoaderType BunLoaderTypeNAPI = 10;
+
#pragma mark - Stream
typedef uint8_t Encoding;
@@ -199,6 +212,32 @@ extern "C" void ZigString__free(const unsigned char* ptr, size_t len, void* allo
extern "C" void Microtask__run(void* ptr, void* global);
extern "C" void Microtask__run_default(void* ptr, void* global);
+extern "C" bool Bun__transpileVirtualModule(
+ JSC::JSGlobalObject* global,
+ ZigString* specifier,
+ ZigString* referrer,
+ ZigString* sourceCode,
+ BunLoaderType loader,
+ ErrorableResolvedSource* result);
+
+extern "C" JSC::EncodedJSValue Bun__runVirtualModule(
+ JSC::JSGlobalObject* global,
+ ZigString* specifier);
+
+extern "C" bool Bun__transpileFile(
+ void* bunVM,
+ JSC::JSGlobalObject* global,
+ ZigString* specifier,
+ ZigString* referrer,
+ ErrorableResolvedSource* result);
+
+extern "C" bool Bun__fetchBuiltinModule(
+ void* bunVM,
+ JSC::JSGlobalObject* global,
+ ZigString* specifier,
+ ZigString* referrer,
+ ErrorableResolvedSource* result);
+
// Used in process.version
extern "C" const char* Bun__version;
diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
index 75b7995f0..456ef6aa4 100644
--- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
@@ -28,6 +28,7 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSink;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStringDecoder;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStringDecoderConstructor;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPendingVirtualModuleResult;
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */
diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
index 42fb1d88b..c5e01c902 100644
--- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
@@ -28,6 +28,7 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForJSSink;
std::unique_ptr<IsoSubspace> m_subspaceForStringDecoder;
std::unique_ptr<IsoSubspace> m_subspaceForStringDecoderConstructor;
+ std::unique_ptr<IsoSubspace> m_subspaceForPendingVirtualModuleResult;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index 2dd7a9d3f..4053b89d5 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -325,6 +325,8 @@ pub const VirtualMachine = struct {
timer: Bun.Timer = Bun.Timer{},
uws_event_loop: ?*uws.Loop = null,
+ is_printing_plugin: bool = false,
+
plugin_runner: ?PluginRunner = null,
/// Do not access this field directly
@@ -596,7 +598,7 @@ pub const VirtualMachine = struct {
// }
- threadlocal var source_code_printer: ?*js_printer.BufferPrinter = null;
+ pub threadlocal var source_code_printer: ?*js_printer.BufferPrinter = null;
pub fn clearRefString(_: *anyopaque, ref_string: *JSC.RefString) void {
_ = VirtualMachine.vm.ref_strings.remove(ref_string.hash);
@@ -676,27 +678,8 @@ pub const VirtualMachine = struct {
const shared_library_suffix = if (Environment.isMac) "dylib" else if (Environment.isLinux) "so" else "";
- const FetchFlags = enum {
- transpile,
- print_source,
- print_source_and_clone,
-
- pub fn disableTranspiling(this: FetchFlags) bool {
- return this != .transpile;
- }
- };
-
- fn _fetch(
- jsc_vm: *VirtualMachine,
- globalObject: *JSGlobalObject,
- _specifier: string,
- _: string,
- log: *logger.Log,
- comptime flags: FetchFlags,
- ) !ResolvedSource {
- std.debug.assert(VirtualMachine.vm_loaded);
- const disable_transpilying = comptime flags.disableTranspiling();
- if (jsc_vm.node_modules != null and strings.eqlComptime(_specifier, bun_file_import_path)) {
+ pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: string, log: *logger.Log, comptime disable_transpilying: bool) !?ResolvedSource {
+ 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:
// - hash
@@ -712,7 +695,7 @@ pub const VirtualMachine = struct {
.source_url = ZigString.init(bun_file_import_path[1..]),
.hash = 0, // TODO
};
- } else if (jsc_vm.node_modules == null and strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) {
+ } else if (jsc_vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) {
return ResolvedSource{
.allocator = null,
.source_code = ZigString.init(Runtime.Runtime.sourceContentBun()),
@@ -720,7 +703,7 @@ pub const VirtualMachine = struct {
.source_url = ZigString.init(Runtime.Runtime.Imports.Name),
.hash = Runtime.Runtime.versionHash(),
};
- } else if (HardcodedModule.Map.get(_specifier)) |hardcoded| {
+ } else if (HardcodedModule.Map.get(specifier)) |hardcoded| {
switch (hardcoded) {
// This is all complicated because the imports have to be linked and we want to run the printer on it
// so it consistently handles bundled imports
@@ -1011,356 +994,61 @@ pub const VirtualMachine = struct {
};
},
}
- } else if (_specifier.len > js_ast.Macro.namespaceWithColon.len and
- strings.eqlComptimeIgnoreLen(_specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon))
+ } else if (specifier.len > js_ast.Macro.namespaceWithColon.len and
+ strings.eqlComptimeIgnoreLen(specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon))
{
if (comptime !disable_transpilying) {
- if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(_specifier))) |entry| {
+ if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(specifier))) |entry| {
return ResolvedSource{
.allocator = null,
.source_code = ZigString.init(entry.source.contents),
- .specifier = ZigString.init(_specifier),
- .source_url = ZigString.init(_specifier),
+ .specifier = ZigString.init(specifier),
+ .source_url = ZigString.init(specifier),
.hash = 0,
};
}
}
}
- var specifier = normalizeSpecifier(_specifier);
+ return null;
+ }
+
+ pub fn fetchWithoutOnLoadPlugins(
+ jsc_vm: *VirtualMachine,
+ _specifier: string,
+ log: *logger.Log,
+ ret: *ErrorableResolvedSource,
+ comptime flags: FetchFlags,
+ ) !ResolvedSource {
+ std.debug.assert(VirtualMachine.vm_loaded);
+
+ if (try fetchBuiltinModule(jsc_vm, _specifier, log, comptime flags.disableTranspiling())) |builtin| {
+ return builtin;
+ }
+
+ var specifier = ModuleLoader.normalizeSpecifier(jsc_vm, _specifier);
var path = Fs.Path.init(specifier);
- const default_loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: {
+ const loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: {
if (strings.eqlLong(specifier, jsc_vm.main, true)) {
break :brk options.Loader.js;
}
break :brk options.Loader.file;
};
- var loader = default_loader;
- var virtual_source: logger.Source = undefined;
- var has_virtual_source = false;
- var source_code_slice: ZigString.Slice = ZigString.Slice.empty;
- defer source_code_slice.deinit();
-
- if (jsc_vm.plugin_runner != null) {
- const namespace = PluginRunner.extractNamespace(_specifier);
- const after_namespace = if (namespace.len == 0)
- specifier
- else
- _specifier[@minimum(namespace.len + 1, _specifier.len)..];
-
- if (PluginRunner.couldBePlugin(_specifier)) {
- if (globalObject.runOnLoadPlugins(ZigString.init(namespace), ZigString.init(after_namespace), .bun)) |plugin_result| {
- if (plugin_result.isException(globalObject.vm()) or plugin_result.isAnyError(globalObject)) {
- jsc_vm.runErrorHandler(plugin_result, null);
- log.addError(null, logger.Loc.Empty, "Failed to run plugin") catch unreachable;
- return error.PluginError;
- }
-
- if (comptime Environment.allow_assert)
- std.debug.assert(plugin_result.isObject());
-
- if (plugin_result.get(globalObject, "loader")) |loader_value| {
- if (!loader_value.isUndefinedOrNull()) {
- const loader_string = loader_value.getZigString(globalObject);
- if (comptime Environment.allow_assert)
- std.debug.assert(loader_string.len > 0);
-
- if (loader_string.eqlComptime("js")) {
- loader = options.Loader.js;
- } else if (loader_string.eqlComptime("jsx")) {
- loader = options.Loader.jsx;
- } else if (loader_string.eqlComptime("tsx")) {
- loader = options.Loader.tsx;
- } else if (loader_string.eqlComptime("ts")) {
- loader = options.Loader.ts;
- } else if (loader_string.eqlComptime("json")) {
- loader = options.Loader.json;
- } else if (loader_string.eqlComptime("toml")) {
- loader = options.Loader.toml;
- } else if (loader_string.eqlComptime("object")) {
- const exports_object: JSValue = @as(?JSValue, brk: {
- const exports_value = plugin_result.get(globalObject, "exports") orelse break :brk null;
- if (!exports_value.isObject()) {
- break :brk null;
- }
- break :brk exports_value;
- }) orelse {
- log.addError(null, logger.Loc.Empty, "Expected object loader to return an \"exports\" object") catch unreachable;
- return error.PluginError;
- };
- return ResolvedSource{
- .allocator = null,
- .source_code = ZigString{
- .ptr = @ptrCast([*]const u8, exports_object.asVoid()),
- .len = 0,
- },
- .specifier = ZigString.init(_specifier),
- .source_url = ZigString.init(_specifier),
- .hash = 0,
- .tag = .object,
- };
- } else {
- log.addErrorFmt(
- null,
- logger.Loc.Empty,
- jsc_vm.allocator,
- "Expected onLoad() plugin \"loader\" to be one of \"js\", \"jsx\", \"tsx\", \"ts\", \"json\", or \"toml\" but received \"{any}\"",
- .{loader_string},
- ) catch unreachable;
- return error.PluginError;
- }
- }
- }
-
- if (plugin_result.get(globalObject, "contents")) |code| {
- if (code.asArrayBuffer(globalObject)) |array_buffer| {
- virtual_source = .{
- .path = path,
- .key_path = path,
- .contents = array_buffer.byteSlice(),
- };
- has_virtual_source = true;
- } else if (code.isString()) {
- source_code_slice = code.toSlice(globalObject, jsc_vm.allocator);
- if (!source_code_slice.allocated) {
- if (!strings.isAllASCII(source_code_slice.slice())) {
- var allocated = try strings.allocateLatin1IntoUTF8(jsc_vm.allocator, []const u8, source_code_slice.slice());
- source_code_slice.ptr = allocated.ptr;
- source_code_slice.len = @truncate(u32, allocated.len);
- source_code_slice.allocated = true;
- source_code_slice.allocator = jsc_vm.allocator;
- }
- }
- virtual_source = .{
- .path = path,
- .key_path = path,
- .contents = source_code_slice.slice(),
- };
- has_virtual_source = true;
- }
- }
-
- if (!has_virtual_source) {
- log.addError(null, logger.Loc.Empty, "Expected onLoad() plugin to return \"contents\" as a string or ArrayBufferView") catch unreachable;
- return error.PluginError;
- }
- } else {
- std.debug.assert(std.fs.path.isAbsolute(specifier)); // if this crashes, it means the resolver was skipped.
- }
- }
- }
-
- const transpiled_result = transpileSourceCode(
+ return try ModuleLoader.transpileSourceCode(
jsc_vm,
specifier,
path,
loader,
log,
- if (has_virtual_source) &virtual_source else null,
+ null,
+ ret,
+ VirtualMachine.source_code_printer.?,
flags,
);
- return transpiled_result;
}
- fn transpileSourceCode(
- jsc_vm: *VirtualMachine,
- specifier: string,
- path: Fs.Path,
- loader: options.Loader,
- log: *logger.Log,
- virtual_source: ?*const logger.Source,
- comptime flags: FetchFlags,
- ) !ResolvedSource {
- const disable_transpilying = comptime flags.disableTranspiling();
-
- switch (loader) {
- .js, .jsx, .ts, .tsx, .json, .toml => {
- jsc_vm.transpiled_count += 1;
- jsc_vm.bundler.resetStore();
- const hash = http.Watcher.getHash(path.text);
-
- var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator;
-
- var fd: ?StoredFileDescriptorType = null;
- var package_json: ?*PackageJSON = null;
-
- if (jsc_vm.watcher) |watcher| {
- if (watcher.indexOf(hash)) |index| {
- const _fd = watcher.watchlist.items(.fd)[index];
- fd = if (_fd > 0) _fd else null;
- package_json = watcher.watchlist.items(.package_json)[index];
- }
- }
-
- var old = jsc_vm.bundler.log;
- jsc_vm.bundler.log = log;
- jsc_vm.bundler.linker.log = log;
- jsc_vm.bundler.resolver.log = log;
-
- defer {
- jsc_vm.bundler.log = old;
- jsc_vm.bundler.linker.log = old;
- jsc_vm.bundler.resolver.log = old;
- }
-
- // this should be a cheap lookup because 24 bytes == 8 * 3 so it's read 3 machine words
- const is_node_override = specifier.len > "/bun-vfs/node_modules/".len and strings.eqlComptimeIgnoreLen(specifier[0.."/bun-vfs/node_modules/".len], "/bun-vfs/node_modules/");
-
- const macro_remappings = if (jsc_vm.macro_mode or !jsc_vm.has_any_macro_remappings or is_node_override)
- MacroRemap{}
- else
- jsc_vm.bundler.options.macro_remap;
-
- var fallback_source: logger.Source = undefined;
-
- var parse_options = Bundler.ParseOptions{
- .allocator = allocator,
- .path = path,
- .loader = loader,
- .dirname_fd = 0,
- .file_descriptor = fd,
- .file_hash = hash,
- .macro_remappings = macro_remappings,
- .jsx = jsc_vm.bundler.options.jsx,
- .virtual_source = virtual_source,
- };
-
- if (is_node_override) {
- if (NodeFallbackModules.contentsFromPath(specifier)) |code| {
- const fallback_path = Fs.Path.initWithNamespace(specifier, "node");
- fallback_source = logger.Source{ .path = fallback_path, .contents = code, .key_path = fallback_path };
- parse_options.virtual_source = &fallback_source;
- }
- }
-
- var parse_result = jsc_vm.bundler.parseMaybeReturnFileOnly(
- parse_options,
- null,
- disable_transpilying,
- ) orelse {
- return error.ParseError;
- };
-
- if (comptime disable_transpilying) {
- return ResolvedSource{
- .allocator = null,
- .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,
- };
- }
-
- const start_count = jsc_vm.bundler.linker.import_counter;
- // We _must_ link because:
- // - node_modules bundle won't be properly
- try jsc_vm.bundler.linker.link(
- path,
- &parse_result,
- jsc_vm.origin,
- .absolute_path,
- false,
- true,
- );
-
- if (!jsc_vm.macro_mode)
- jsc_vm.resolved_count += jsc_vm.bundler.linker.import_counter - start_count;
- jsc_vm.bundler.linker.import_counter = 0;
-
- var printer = source_code_printer.?.*;
- printer.ctx.reset();
-
- const written = brk: {
- defer source_code_printer.?.* = printer;
- break :brk try jsc_vm.bundler.printWithSourceMap(
- parse_result,
- @TypeOf(&printer),
- &printer,
- .esm_ascii,
- SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings),
- );
- };
-
- if (written == 0) {
- return error.PrintingErrorWriteFailed;
- }
-
- if (jsc_vm.has_loaded) {
- return jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null);
- }
-
- return ResolvedSource{
- .allocator = null,
- .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())),
- .specifier = ZigString.init(specifier),
- .source_url = ZigString.init(path.text),
- // // TODO: change hash to a bitfield
- // .hash = 1,
-
- // having JSC own the memory causes crashes
- .hash = 0,
- };
- },
- // provideFetch() should be called
- .napi => unreachable,
- // .wasm => {
- // jsc_vm.transpiled_count += 1;
- // var fd: ?StoredFileDescriptorType = null;
-
- // var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator;
-
- // const hash = http.Watcher.getHash(path.text);
- // if (jsc_vm.watcher) |watcher| {
- // if (watcher.indexOf(hash)) |index| {
- // const _fd = watcher.watchlist.items(.fd)[index];
- // fd = if (_fd > 0) _fd else null;
- // }
- // }
-
- // var parse_options = Bundler.ParseOptions{
- // .allocator = allocator,
- // .path = path,
- // .loader = loader,
- // .dirname_fd = 0,
- // .file_descriptor = fd,
- // .file_hash = hash,
- // .macro_remappings = MacroRemap{},
- // .jsx = jsc_vm.bundler.options.jsx,
- // };
-
- // var parse_result = jsc_vm.bundler.parse(
- // parse_options,
- // null,
- // ) orelse {
- // return error.ParseError;
- // };
-
- // return ResolvedSource{
- // .allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null,
- // .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable),
- // .specifier = ZigString.init(specifier),
- // .source_url = ZigString.init(path.text),
- // .hash = 0,
- // .tag = ResolvedSource.Tag.wasm,
- // };
- // },
- else => {
- return ResolvedSource{
- .allocator = &vm.allocator,
- .source_code = ZigString.init(try strings.quotedAlloc(jsc_vm.allocator, path.pretty)),
- .specifier = ZigString.init(path.text),
- .source_url = ZigString.init(path.text),
- .hash = 0,
- };
- },
- }
- }
pub const ResolveFunctionResult = struct {
result: ?Resolver.Result,
path: string,
@@ -1546,42 +1234,6 @@ pub const VirtualMachine = struct {
res.* = ErrorableZigString.ok(ZigString.init(result.path));
}
- pub fn normalizeSpecifier(slice_: string) string {
- var vm_ = VirtualMachine.vm;
-
- var slice = slice_;
- if (slice.len == 0) return slice;
- var was_http = false;
- if (strings.hasPrefixComptime(slice, "https://")) {
- slice = slice["https://".len..];
- was_http = true;
- } else if (strings.hasPrefixComptime(slice, "http://")) {
- slice = slice["http://".len..];
- was_http = true;
- }
-
- if (strings.hasPrefix(slice, vm_.origin.host)) {
- slice = slice[vm_.origin.host.len..];
- } else if (was_http) {
- if (strings.indexOfChar(slice, '/')) |i| {
- slice = slice[i..];
- }
- }
-
- if (vm_.origin.path.len > 1) {
- if (strings.hasPrefix(slice, vm_.origin.path)) {
- slice = slice[vm_.origin.path.len..];
- }
- }
-
- if (vm_.bundler.options.routes.asset_prefix_path.len > 0) {
- if (strings.hasPrefix(slice, vm_.bundler.options.routes.asset_prefix_path)) {
- slice = slice[vm_.bundler.options.routes.asset_prefix_path.len..];
- }
- }
-
- return slice;
- }
// // This double prints
// pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, _: JSPromiseRejectionOperation) callconv(.C) JSValue {
@@ -1605,12 +1257,12 @@ pub const VirtualMachine = struct {
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| {
+ @call(.{ .modifier = .always_inline }, fetchWithoutOnLoadPlugins, .{ jsc_vm, spec, &log, ret, .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| {
+ fetchWithoutOnLoadPlugins(jsc_vm, spec, &log, ret, .print_source_and_clone) catch |err| {
processFetchLog(global, specifier, source, &log, ret, err);
return;
};
@@ -1657,7 +1309,7 @@ pub const VirtualMachine = struct {
ret.success = true;
}
- fn processFetchLog(globalThis: *JSGlobalObject, specifier: ZigString, referrer: ZigString, log: *logger.Log, ret: *ErrorableResolvedSource, err: anyerror) void {
+ pub fn processFetchLog(globalThis: *JSGlobalObject, specifier: ZigString, referrer: ZigString, log: *logger.Log, ret: *ErrorableResolvedSource, err: anyerror) void {
switch (log.msgs.items.len) {
0 => {
const msg = logger.Msg{
@@ -2074,7 +1726,8 @@ pub const VirtualMachine = struct {
@maximum(top.position.column_start, 0),
)) |mapping| {
var log = logger.Log.init(default_allocator);
- var original_source = _fetch(this, this.global, top.source_url.slice(), "", &log, .print_source) catch return;
+ var errorable: ErrorableResolvedSource = undefined;
+ var original_source = fetchWithoutOnLoadPlugins(this, top.source_url.slice(), &log, &errorable, .print_source) catch return;
const code = original_source.source_code.slice();
top.position.line = mapping.original.lines;
top.position.line_start = mapping.original.lines;
@@ -3091,3 +2744,528 @@ inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag) ResolvedSource {
.tag = name,
};
}
+
+fn dumpSource(specifier: string, printer: anytype) !void {
+ const BunDebugHolder = struct {
+ pub var dir: ?std.fs.Dir = null;
+ };
+ if (BunDebugHolder.dir == null) {
+ BunDebugHolder.dir = try std.fs.cwd().makeOpenPath("/tmp/bun-debug-src/", .{ .iterate = true });
+ }
+
+ if (std.fs.path.dirname(specifier)) |dir_path| {
+ var parent = try BunDebugHolder.dir.?.makeOpenPath(dir_path[1..], .{ .iterate = true });
+ defer parent.close();
+ try parent.writeFile(std.fs.path.basename(specifier), printer.ctx.getWritten());
+ } else {
+ try BunDebugHolder.dir.?.writeFile(std.fs.path.basename(specifier), printer.ctx.getWritten());
+ }
+}
+
+pub const ModuleLoader = struct {
+ pub fn transpileSourceCode(
+ jsc_vm: *VirtualMachine,
+ specifier: string,
+ path: Fs.Path,
+ loader: options.Loader,
+ log: *logger.Log,
+ virtual_source: ?*const logger.Source,
+ ret: *ErrorableResolvedSource,
+ source_code_printer: *js_printer.BufferPrinter,
+ comptime flags: FetchFlags,
+ ) !ResolvedSource {
+ const disable_transpilying = comptime flags.disableTranspiling();
+
+ switch (loader) {
+ .js, .jsx, .ts, .tsx, .json, .toml => {
+ jsc_vm.transpiled_count += 1;
+ jsc_vm.bundler.resetStore();
+ const hash = http.Watcher.getHash(path.text);
+
+ var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator;
+
+ var fd: ?StoredFileDescriptorType = null;
+ var package_json: ?*PackageJSON = null;
+
+ if (jsc_vm.watcher) |watcher| {
+ if (watcher.indexOf(hash)) |index| {
+ const _fd = watcher.watchlist.items(.fd)[index];
+ fd = if (_fd > 0) _fd else null;
+ package_json = watcher.watchlist.items(.package_json)[index];
+ }
+ }
+
+ var old = jsc_vm.bundler.log;
+ jsc_vm.bundler.log = log;
+ jsc_vm.bundler.linker.log = log;
+ jsc_vm.bundler.resolver.log = log;
+
+ defer {
+ jsc_vm.bundler.log = old;
+ jsc_vm.bundler.linker.log = old;
+ jsc_vm.bundler.resolver.log = old;
+ }
+
+ // this should be a cheap lookup because 24 bytes == 8 * 3 so it's read 3 machine words
+ const is_node_override = specifier.len > "/bun-vfs/node_modules/".len and strings.eqlComptimeIgnoreLen(specifier[0.."/bun-vfs/node_modules/".len], "/bun-vfs/node_modules/");
+
+ const macro_remappings = if (jsc_vm.macro_mode or !jsc_vm.has_any_macro_remappings or is_node_override)
+ MacroRemap{}
+ else
+ jsc_vm.bundler.options.macro_remap;
+
+ var fallback_source: logger.Source = undefined;
+
+ var parse_options = Bundler.ParseOptions{
+ .allocator = allocator,
+ .path = path,
+ .loader = loader,
+ .dirname_fd = 0,
+ .file_descriptor = fd,
+ .file_hash = hash,
+ .macro_remappings = macro_remappings,
+ .jsx = jsc_vm.bundler.options.jsx,
+ .virtual_source = virtual_source,
+ .hoist_bun_plugin = true,
+ };
+
+ if (is_node_override) {
+ if (NodeFallbackModules.contentsFromPath(specifier)) |code| {
+ const fallback_path = Fs.Path.initWithNamespace(specifier, "node");
+ fallback_source = logger.Source{ .path = fallback_path, .contents = code, .key_path = fallback_path };
+ parse_options.virtual_source = &fallback_source;
+ }
+ }
+
+ var parse_result = jsc_vm.bundler.parseMaybeReturnFileOnly(
+ parse_options,
+ null,
+ disable_transpilying,
+ ) orelse {
+ return error.ParseError;
+ };
+
+ if (comptime disable_transpilying) {
+ return ResolvedSource{
+ .allocator = null,
+ .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,
+ };
+ }
+
+ const has_bun_plugin = parse_result.ast.bun_plugin.hoisted_stmts.items.len > 0;
+
+ if (has_bun_plugin) {
+ try ModuleLoader.runBunPlugin(jsc_vm, source_code_printer, &parse_result, ret);
+ }
+
+ var printer = source_code_printer.*;
+ printer.ctx.reset();
+
+ const start_count = jsc_vm.bundler.linker.import_counter;
+ // We _must_ link because:
+ // - node_modules bundle won't be properly
+ try jsc_vm.bundler.linker.link(
+ path,
+ &parse_result,
+ jsc_vm.origin,
+ .absolute_path,
+ false,
+ true,
+ );
+
+ if (!jsc_vm.macro_mode)
+ jsc_vm.resolved_count += jsc_vm.bundler.linker.import_counter - start_count;
+ jsc_vm.bundler.linker.import_counter = 0;
+
+ const written = brk: {
+ defer source_code_printer.* = printer;
+ break :brk try jsc_vm.bundler.printWithSourceMap(
+ parse_result,
+ @TypeOf(&printer),
+ &printer,
+ .esm_ascii,
+ SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings),
+ );
+ };
+
+ if (written == 0) {
+ // if it's an empty file but there were plugins
+ // we don't want it to break if you try to import from it
+ if (has_bun_plugin) {
+ return ResolvedSource{
+ .allocator = null,
+ .source_code = ZigString.init("// auto-generated plugin stub\nexport default undefined\n"),
+ .specifier = ZigString.init(specifier),
+ .source_url = ZigString.init(path.text),
+ // // TODO: change hash to a bitfield
+ // .hash = 1,
+
+ // having JSC own the memory causes crashes
+ .hash = 0,
+ };
+ }
+ return error.PrintingErrorWriteFailed;
+ }
+
+ if (jsc_vm.has_loaded) {
+ return jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null);
+ }
+
+ if (comptime Environment.dump_source) {
+ try dumpSource(specifier, &printer);
+ }
+
+ return ResolvedSource{
+ .allocator = null,
+ .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())),
+ .specifier = ZigString.init(specifier),
+ .source_url = ZigString.init(path.text),
+ // // TODO: change hash to a bitfield
+ // .hash = 1,
+
+ // having JSC own the memory causes crashes
+ .hash = 0,
+ };
+ },
+ // provideFetch() should be called
+ .napi => unreachable,
+ // .wasm => {
+ // jsc_vm.transpiled_count += 1;
+ // var fd: ?StoredFileDescriptorType = null;
+
+ // var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator;
+
+ // const hash = http.Watcher.getHash(path.text);
+ // if (jsc_vm.watcher) |watcher| {
+ // if (watcher.indexOf(hash)) |index| {
+ // const _fd = watcher.watchlist.items(.fd)[index];
+ // fd = if (_fd > 0) _fd else null;
+ // }
+ // }
+
+ // var parse_options = Bundler.ParseOptions{
+ // .allocator = allocator,
+ // .path = path,
+ // .loader = loader,
+ // .dirname_fd = 0,
+ // .file_descriptor = fd,
+ // .file_hash = hash,
+ // .macro_remappings = MacroRemap{},
+ // .jsx = jsc_vm.bundler.options.jsx,
+ // };
+
+ // var parse_result = jsc_vm.bundler.parse(
+ // parse_options,
+ // null,
+ // ) orelse {
+ // return error.ParseError;
+ // };
+
+ // return ResolvedSource{
+ // .allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null,
+ // .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable),
+ // .specifier = ZigString.init(specifier),
+ // .source_url = ZigString.init(path.text),
+ // .hash = 0,
+ // .tag = ResolvedSource.Tag.wasm,
+ // };
+ // },
+ else => {
+ return ResolvedSource{
+ .allocator = &jsc_vm.allocator,
+ .source_code = ZigString.init(try strings.quotedAlloc(jsc_vm.allocator, path.pretty)),
+ .specifier = ZigString.init(path.text),
+ .source_url = ZigString.init(path.text),
+ .hash = 0,
+ };
+ },
+ }
+ }
+
+ pub fn runBunPlugin(
+ jsc_vm: *VirtualMachine,
+ source_code_printer: *js_printer.BufferPrinter,
+ parse_result: *ParseResult,
+ ret: *ErrorableResolvedSource,
+ ) !void {
+ var printer = source_code_printer.*;
+ printer.ctx.reset();
+
+ defer printer.ctx.reset();
+ // If we start transpiling in the middle of an existing transpilation session
+ // we will hit undefined memory bugs
+ // unless we disable resetting the store until we are done transpiling
+ const prev_disable_reset = js_ast.Stmt.Data.Store.disable_reset;
+ js_ast.Stmt.Data.Store.disable_reset = true;
+ js_ast.Expr.Data.Store.disable_reset = true;
+
+ // flip the source code we use
+ // unless we're already transpiling a plugin
+ // that case could happen when
+ const was_printing_plugin = jsc_vm.is_printing_plugin;
+ const prev = jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache;
+ jsc_vm.is_printing_plugin = true;
+ defer {
+ js_ast.Stmt.Data.Store.disable_reset = prev_disable_reset;
+ js_ast.Expr.Data.Store.disable_reset = prev_disable_reset;
+ if (!was_printing_plugin) jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache = prev;
+ jsc_vm.is_printing_plugin = was_printing_plugin;
+ }
+ // we flip use_alternate_source_cache
+ if (!was_printing_plugin) jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache = !prev;
+
+ // this is a bad idea, but it should work for now.
+ const original_name = parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name;
+ parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name = "globalThis.Bun.plugin";
+ defer {
+ parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name = original_name;
+ }
+ const hoisted_stmts = parse_result.ast.bun_plugin.hoisted_stmts.items;
+
+ var parts = [1]js_ast.Part{
+ js_ast.Part{
+ .stmts = hoisted_stmts,
+ },
+ };
+ var ast_copy = parse_result.ast;
+ ast_copy.parts = &parts;
+ ast_copy.prepend_part = null;
+ var temporary_source = parse_result.source;
+ var source_name = try std.fmt.allocPrint(jsc_vm.allocator, "{s}.plugin.{s}", .{ temporary_source.path.text, temporary_source.path.name.ext[1..] });
+ temporary_source.path = Fs.Path.init(source_name);
+
+ _ = brk: {
+ defer source_code_printer.* = printer;
+ break :brk try jsc_vm.bundler.printWithSourceMapMaybe(
+ ast_copy,
+ &temporary_source,
+ @TypeOf(&printer),
+ &printer,
+ .esm_ascii,
+ true,
+ SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings),
+ );
+ };
+ const wrote = printer.ctx.getWritten();
+
+ if (wrote.len > 0) {
+ if (comptime Environment.dump_source)
+ try dumpSource(temporary_source.path.text, &printer);
+
+ var exception = [1]JSC.JSValue{JSC.JSValue.zero};
+ _ = JSC.JSModuleLoader.evaluate(
+ jsc_vm.global,
+ wrote.ptr,
+ wrote.len,
+ temporary_source.path.text.ptr,
+ temporary_source.path.text.len,
+ JSC.JSValue.jsUndefined(),
+ &exception,
+ );
+ if (!exception[0].isEmpty()) {
+ ret.* = JSC.ErrorableResolvedSource.err(
+ error.JSErrorObject,
+ exception[0].asVoid(),
+ );
+ return error.PluginError;
+ }
+ }
+ }
+ pub fn normalizeSpecifier(jsc_vm: *VirtualMachine, slice_: string) string {
+ var slice = slice_;
+ if (slice.len == 0) return slice;
+ var was_http = false;
+ if (strings.hasPrefixComptime(slice, "https://")) {
+ slice = slice["https://".len..];
+ was_http = true;
+ } else if (strings.hasPrefixComptime(slice, "http://")) {
+ slice = slice["http://".len..];
+ was_http = true;
+ }
+
+ if (strings.hasPrefix(slice, jsc_vm.origin.host)) {
+ slice = slice[jsc_vm.origin.host.len..];
+ } else if (was_http) {
+ if (strings.indexOfChar(slice, '/')) |i| {
+ slice = slice[i..];
+ }
+ }
+
+ if (jsc_vm.origin.path.len > 1) {
+ if (strings.hasPrefix(slice, jsc_vm.origin.path)) {
+ slice = slice[jsc_vm.origin.path.len..];
+ }
+ }
+
+ if (jsc_vm.bundler.options.routes.asset_prefix_path.len > 0) {
+ if (strings.hasPrefix(slice, jsc_vm.bundler.options.routes.asset_prefix_path)) {
+ slice = slice[jsc_vm.bundler.options.routes.asset_prefix_path.len..];
+ }
+ }
+
+ return slice;
+ }
+
+ pub export fn Bun__fetchBuiltinModule(
+ jsc_vm: *VirtualMachine,
+ globalObject: *JSC.JSGlobalObject,
+ specifier: *ZigString,
+ referrer: *ZigString,
+ ret: *ErrorableResolvedSource,
+ ) bool {
+ JSC.markBinding();
+ var log = logger.Log.init(jsc_vm.bundler.allocator);
+ defer log.deinit();
+ if (jsc_vm.fetchBuiltinModule(specifier.slice(), &log, true) catch |err| {
+ VirtualMachine.processFetchLog(globalObject, specifier.*, referrer.*, &log, ret, err);
+ return true;
+ }) |builtin| {
+ ret.* = ErrorableResolvedSource.ok(builtin);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ pub export fn Bun__transpileFile(
+ jsc_vm: *VirtualMachine,
+ globalObject: *JSC.JSGlobalObject,
+ specifier_ptr: *ZigString,
+ referrer: *ZigString,
+ ret: *ErrorableResolvedSource,
+ ) bool {
+ JSC.markBinding();
+ var log = logger.Log.init(jsc_vm.bundler.allocator);
+ defer log.deinit();
+ var _specifier = specifier_ptr.toSlice(jsc_vm.allocator);
+ defer _specifier.deinit();
+ var specifier = normalizeSpecifier(jsc_vm, _specifier.slice());
+ const path = Fs.Path.init(specifier);
+ const loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: {
+ if (strings.eqlLong(specifier, jsc_vm.main, true)) {
+ break :brk options.Loader.js;
+ }
+
+ break :brk options.Loader.file;
+ };
+ ret.* = ErrorableResolvedSource.ok(
+ ModuleLoader.transpileSourceCode(
+ jsc_vm,
+ specifier,
+ path,
+ loader,
+ &log,
+ null,
+ ret,
+ VirtualMachine.source_code_printer.?,
+ FetchFlags.transpile,
+ ) catch |err| {
+ if (err == error.PluginERror) {
+ return true;
+ }
+ VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err);
+ return true;
+ },
+ );
+ return true;
+ }
+
+ export fn Bun__runVirtualModule(globalObject: *JSC.JSGlobalObject, specifier_ptr: *ZigString) JSValue {
+ JSC.markBinding();
+ if (globalObject.bunVM().plugin_runner == null) return JSValue.zero;
+
+ const specifier = specifier_ptr.slice();
+
+ if (!PluginRunner.couldBePlugin(specifier)) {
+ return JSValue.zero;
+ }
+
+ const namespace = PluginRunner.extractNamespace(specifier);
+ const after_namespace = if (namespace.len == 0)
+ specifier
+ else
+ specifier[@minimum(namespace.len + 1, specifier.len)..];
+
+ return globalObject.runOnLoadPlugins(ZigString.init(namespace), ZigString.init(after_namespace), .bun) orelse return JSValue.zero;
+ }
+
+ export fn Bun__transpileVirtualModule(
+ globalObject: *JSC.JSGlobalObject,
+ specifier_ptr: *ZigString,
+ referrer_ptr: *ZigString,
+ source_code: *ZigString,
+ loader_: Api.Loader,
+ ret: *ErrorableResolvedSource,
+ ) bool {
+ JSC.markBinding();
+ const jsc_vm = globalObject.bunVM();
+ std.debug.assert(jsc_vm.plugin_runner != null);
+
+ var specifier_slice = specifier_ptr.toSlice(jsc_vm.allocator);
+ const specifier = specifier_slice.slice();
+ defer specifier_slice.deinit();
+ var source_code_slice = source_code.toSlice(jsc_vm.allocator);
+ defer source_code_slice.deinit();
+
+ var virtual_source = logger.Source.initPathString(specifier, source_code_slice.slice());
+ var log = logger.Log.init(jsc_vm.allocator);
+ const path = Fs.Path.init(specifier);
+
+ const loader = if (loader_ != ._none)
+ options.Loader.fromString(@tagName(loader_)).?
+ else
+ jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: {
+ if (strings.eqlLong(specifier, jsc_vm.main, true)) {
+ break :brk options.Loader.js;
+ }
+
+ break :brk options.Loader.file;
+ };
+
+ defer log.deinit();
+ ret.* = ErrorableResolvedSource.ok(
+ ModuleLoader.transpileSourceCode(
+ jsc_vm,
+ specifier,
+ path,
+ options.Loader.fromString(@tagName(loader)).?,
+ &log,
+ &virtual_source,
+ ret,
+ VirtualMachine.source_code_printer.?,
+ FetchFlags.transpile,
+ ) catch |err| {
+ if (err == error.PluginError) {
+ return true;
+ }
+ VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err);
+ return true;
+ },
+ );
+ return true;
+ }
+
+ comptime {
+ _ = Bun__transpileVirtualModule;
+ _ = Bun__runVirtualModule;
+ _ = Bun__transpileFile;
+ _ = Bun__fetchBuiltinModule;
+ }
+};
+
+const FetchFlags = enum {
+ transpile,
+ print_source,
+ print_source_and_clone,
+
+ pub fn disableTranspiling(this: FetchFlags) bool {
+ return this != .transpile;
+ }
+};
diff --git a/src/bun.js/modules/BufferModule.h b/src/bun.js/modules/BufferModule.h
index 9032f0d2e..8a9bf8c27 100644
--- a/src/bun.js/modules/BufferModule.h
+++ b/src/bun.js/modules/BufferModule.h
@@ -1,33 +1,46 @@
-#include "../bindings/ZigGlobalObject.h"
#include "../bindings/JSBuffer.h"
+#include "../bindings/ZigGlobalObject.h"
#include "JavaScriptCore/JSGlobalObject.h"
namespace Zig {
-
-inline void generateBufferSourceCode(JSC::JSGlobalObject* lexicalGlobalObject, JSC::Identifier moduleKey, Vector<JSC::Identifier, 4>& exportNames, JSC::MarkedArgumentBuffer& exportValues) {
- JSC::VM& vm = lexicalGlobalObject->vm();
- GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject);
-
- exportNames.append(JSC::Identifier::fromString(vm, "Buffer"_s));
- exportValues.append(WebCore::JSBuffer::getConstructor(vm, globalObject));
-
- auto* slowBuffer = JSC::JSFunction::create(vm, globalObject, 0, "SlowBuffer"_s, WebCore::constructSlowBuffer, ImplementationVisibility::Public, NoIntrinsic, WebCore::constructSlowBuffer);
- slowBuffer->putDirect(vm, vm.propertyNames->prototype, WebCore::JSBuffer::prototype(vm, *jsCast<JSDOMGlobalObject*>(lexicalGlobalObject)), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
- exportNames.append(JSC::Identifier::fromString(vm, "SlowBuffer"_s));
- exportValues.append(slowBuffer);
-
- // substitute after JSBlob is implemented.
- exportNames.append(JSC::Identifier::fromString(vm, "Blob"_s));
- exportValues.append(JSC::jsUndefined());
-
- exportNames.append(JSC::Identifier::fromString(vm, "INSPECT_MAX_BYTES"_s));
- exportValues.append(JSC::jsNumber(50));
-
- exportNames.append(JSC::Identifier::fromString(vm, "kMaxLength"_s));
- exportValues.append(JSC::jsNumber(4294967296LL));
-
- exportNames.append(JSC::Identifier::fromString(vm, "kMaxLength"_s));
- exportValues.append(JSC::jsNumber(536870888));
+using namespace WebCore;
+
+inline void generateBufferSourceCode(JSC::JSGlobalObject *lexicalGlobalObject,
+ JSC::Identifier moduleKey,
+ Vector<JSC::Identifier, 4> &exportNames,
+ JSC::MarkedArgumentBuffer &exportValues) {
+ JSC::VM &vm = lexicalGlobalObject->vm();
+ GlobalObject *globalObject =
+ reinterpret_cast<GlobalObject *>(lexicalGlobalObject);
+
+ exportNames.append(JSC::Identifier::fromString(vm, "Buffer"_s));
+ exportValues.append(WebCore::JSBuffer::getConstructor(vm, globalObject));
+
+ auto *slowBuffer = JSC::JSFunction::create(
+ vm, globalObject, 0, "SlowBuffer"_s, WebCore::constructSlowBuffer,
+ ImplementationVisibility::Public, NoIntrinsic,
+ WebCore::constructSlowBuffer);
+ slowBuffer->putDirect(
+ vm, vm.propertyNames->prototype,
+ WebCore::JSBuffer::prototype(
+ vm, *jsCast<JSDOMGlobalObject *>(lexicalGlobalObject)),
+ JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum |
+ JSC::PropertyAttribute::DontDelete);
+ exportNames.append(JSC::Identifier::fromString(vm, "SlowBuffer"_s));
+ exportValues.append(slowBuffer);
+
+ // substitute after JSBlob is implemented.
+ exportNames.append(JSC::Identifier::fromString(vm, "Blob"_s));
+ exportValues.append(JSC::jsUndefined());
+
+ exportNames.append(JSC::Identifier::fromString(vm, "INSPECT_MAX_BYTES"_s));
+ exportValues.append(JSC::jsNumber(50));
+
+ exportNames.append(JSC::Identifier::fromString(vm, "kMaxLength"_s));
+ exportValues.append(JSC::jsNumber(4294967296LL));
+
+ exportNames.append(JSC::Identifier::fromString(vm, "kMaxLength"_s));
+ exportValues.append(JSC::jsNumber(536870888));
}
-}
+} // namespace Zig
diff --git a/src/bun.js/modules/EventsModule.h b/src/bun.js/modules/EventsModule.h
index 5adb19d01..d1e14a2db 100644
--- a/src/bun.js/modules/EventsModule.h
+++ b/src/bun.js/modules/EventsModule.h
@@ -1,27 +1,36 @@
-#include "../bindings/ZigGlobalObject.h"
#include "JavaScriptCore/JSGlobalObject.h"
+#include "ZigGlobalObject.h"
namespace Zig {
+using namespace WebCore;
+inline void generateEventsSourceCode(JSC::JSGlobalObject *lexicalGlobalObject,
+ JSC::Identifier moduleKey,
+ Vector<JSC::Identifier, 4> &exportNames,
+ JSC::MarkedArgumentBuffer &exportValues) {
+ JSC::VM &vm = lexicalGlobalObject->vm();
+ GlobalObject *globalObject =
+ reinterpret_cast<GlobalObject *>(lexicalGlobalObject);
-inline void generateEventsSourceCode(JSC::JSGlobalObject* lexicalGlobalObject, JSC::Identifier moduleKey, Vector<JSC::Identifier, 4>& exportNames, JSC::MarkedArgumentBuffer& exportValues) {
- JSC::VM& vm = lexicalGlobalObject->vm();
- GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject);
+ exportNames.append(JSC::Identifier::fromString(vm, "EventEmitter"_s));
+ exportValues.append(
+ WebCore::JSEventEmitter::getConstructor(vm, globalObject));
- exportNames.append(JSC::Identifier::fromString(vm, "EventEmitter"_s));
- exportValues.append(WebCore::JSEventEmitter::getConstructor(vm, globalObject));
-
- exportNames.append(JSC::Identifier::fromString(vm, "getEventListeners"_s));
- exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
- MAKE_STATIC_STRING_IMPL("getEventListeners"), Events_functionGetEventListeners, ImplementationVisibility::Public));
- exportNames.append(JSC::Identifier::fromString(vm, "listenerCount"_s));
- exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
- MAKE_STATIC_STRING_IMPL("listenerCount"), Events_functionListenerCount, ImplementationVisibility::Public));
- exportNames.append(JSC::Identifier::fromString(vm, "once"_s));
- exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
- MAKE_STATIC_STRING_IMPL("once"), Events_functionOnce, ImplementationVisibility::Public));
- exportNames.append(JSC::Identifier::fromString(vm, "on"_s));
- exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
- MAKE_STATIC_STRING_IMPL("on"), Events_functionOn, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "getEventListeners"_s));
+ exportValues.append(JSC::JSFunction::create(
+ vm, lexicalGlobalObject, 0, MAKE_STATIC_STRING_IMPL("getEventListeners"),
+ Events_functionGetEventListeners, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "listenerCount"_s));
+ exportValues.append(JSC::JSFunction::create(
+ vm, lexicalGlobalObject, 0, MAKE_STATIC_STRING_IMPL("listenerCount"),
+ Events_functionListenerCount, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "once"_s));
+ exportValues.append(JSC::JSFunction::create(
+ vm, lexicalGlobalObject, 0, MAKE_STATIC_STRING_IMPL("once"),
+ Events_functionOnce, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "on"_s));
+ exportValues.append(JSC::JSFunction::create(
+ vm, lexicalGlobalObject, 0, MAKE_STATIC_STRING_IMPL("on"),
+ Events_functionOn, ImplementationVisibility::Public));
}
-}
+} // namespace Zig
diff --git a/src/bundler.zig b/src/bundler.zig
index c5b593507..86223ea60 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -859,7 +859,8 @@ pub const Bundler = struct {
return BuildResolveResultPair{
.written = switch (result.ast.exports_kind) {
.esm => try bundler.printWithSourceMapMaybe(
- result,
+ result.ast,
+ &result.source,
Writer,
writer,
.esm_ascii,
@@ -867,7 +868,8 @@ pub const Bundler = struct {
source_map_handler,
),
.cjs => try bundler.printWithSourceMapMaybe(
- result,
+ result.ast,
+ &result.source,
Writer,
writer,
.cjs_ascii,
@@ -885,7 +887,8 @@ pub const Bundler = struct {
return BuildResolveResultPair{
.written = switch (result.ast.exports_kind) {
.none, .esm => try bundler.printWithSourceMapMaybe(
- result,
+ result.ast,
+ &result.source,
Writer,
writer,
.esm,
@@ -893,7 +896,8 @@ pub const Bundler = struct {
source_map_handler,
),
.cjs => try bundler.printWithSourceMapMaybe(
- result,
+ result.ast,
+ &result.source,
Writer,
writer,
.cjs,
@@ -1101,14 +1105,14 @@ pub const Bundler = struct {
pub fn printWithSourceMapMaybe(
bundler: *ThisBundler,
- result: ParseResult,
+ ast: js_ast.Ast,
+ source: *const logger.Source,
comptime Writer: type,
writer: Writer,
comptime format: js_printer.Format,
comptime enable_source_map: bool,
source_map_context: ?js_printer.SourceMapHandler,
) !usize {
- const ast = result.ast;
var symbols: [][]js_ast.Symbol = &([_][]js_ast.Symbol{ast.symbols});
return switch (format) {
@@ -1117,7 +1121,7 @@ pub const Bundler = struct {
writer,
ast,
js_ast.Symbol.Map.initList(symbols),
- &result.source,
+ source,
false,
js_printer.Options{
.to_module_ref = Ref.RuntimeRef,
@@ -1138,7 +1142,7 @@ pub const Bundler = struct {
writer,
ast,
js_ast.Symbol.Map.initList(symbols),
- &result.source,
+ source,
false,
js_printer.Options{
.to_module_ref = Ref.RuntimeRef,
@@ -1159,7 +1163,7 @@ pub const Bundler = struct {
writer,
ast,
js_ast.Symbol.Map.initList(symbols),
- &result.source,
+ source,
true,
js_printer.Options{
.to_module_ref = Ref.RuntimeRef,
@@ -1180,7 +1184,7 @@ pub const Bundler = struct {
writer,
ast,
js_ast.Symbol.Map.initList(symbols),
- &result.source,
+ source,
false,
js_printer.Options{
.to_module_ref = Ref.RuntimeRef,
@@ -1201,7 +1205,7 @@ pub const Bundler = struct {
writer,
ast,
js_ast.Symbol.Map.initList(symbols),
- &result.source,
+ source,
true,
js_printer.Options{
.to_module_ref = Ref.RuntimeRef,
@@ -1222,7 +1226,7 @@ pub const Bundler = struct {
writer,
ast,
js_ast.Symbol.Map.initList(symbols),
- &result.source,
+ source,
false,
js_printer.Options{
.to_module_ref = Ref.RuntimeRef,
@@ -1248,7 +1252,8 @@ pub const Bundler = struct {
comptime format: js_printer.Format,
) !usize {
return bundler.printWithSourceMapMaybe(
- result,
+ result.ast,
+ &result.source,
Writer,
writer,
format,
@@ -1266,7 +1271,8 @@ pub const Bundler = struct {
handler: js_printer.SourceMapHandler,
) !usize {
return bundler.printWithSourceMapMaybe(
- result,
+ result.ast,
+ &result.source,
Writer,
writer,
format,
@@ -1287,6 +1293,7 @@ pub const Bundler = struct {
macro_js_ctx: MacroJSValueType = default_macro_js_value,
virtual_source: ?*const logger.Source = null,
replace_exports: runtime.Runtime.Features.ReplaceableExport.Map = .{},
+ hoist_bun_plugin: bool = false,
};
pub fn parse(
@@ -1403,7 +1410,7 @@ pub const Bundler = struct {
(jsx.runtime == .automatic or jsx.runtime == .classic);
opts.features.jsx_optimization_hoist = bundler.options.jsx_optimization_hoist orelse opts.features.jsx_optimization_inline;
-
+ opts.features.hoist_bun_plugin = this_parse.hoist_bun_plugin;
if (bundler.macro_context == null) {
bundler.macro_context = js_ast.Macro.MacroContext.init(bundler);
}
diff --git a/src/env.zig b/src/env.zig
index 904c80006..d168865ab 100644
--- a/src/env.zig
+++ b/src/env.zig
@@ -36,3 +36,4 @@ pub const baseline = BuildOptions.baseline;
pub const enableSIMD: bool = !baseline;
pub const git_sha = BuildOptions.sha;
pub const is_canary = BuildOptions.is_canary;
+pub const dump_source = isDebug and !isTest;
diff --git a/src/fs.zig b/src/fs.zig
index e54c7e0e6..59f162609 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -286,7 +286,7 @@ pub const FileSystem = struct {
var scratch_lookup_buffer: [256]u8 = undefined;
std.debug.assert(scratch_lookup_buffer.len >= _query.len);
- const query = strings.copyLowercase(_query, &scratch_lookup_buffer);
+ const query = strings.copyLowercaseIfNeeded(_query, &scratch_lookup_buffer);
const result = entry.data.get(query) orelse return null;
const basename = result.base();
if (!strings.eql(basename, _query)) {
@@ -810,7 +810,7 @@ pub const FileSystem = struct {
// This custom map implementation:
// - Preallocates a fixed amount of directory name space
// - Doesn't store directory names which don't exist.
- pub const Map = allocators.BSSMap(EntriesOption, Preallocate.Counts.dir_entry, false, 128, true);
+ pub const Map = allocators.BSSMap(EntriesOption, Preallocate.Counts.dir_entry, false, 256, true);
};
pub fn openDir(_: *RealFS, unsafe_dir_string: string) std.fs.File.OpenError!std.fs.Dir {
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 7ff8476ac..30ebffaea 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -4391,7 +4391,10 @@ pub const Ast = struct {
wrapper_ref: ?Ref = null,
require_ref: Ref = Ref.None,
+ bun_plugin: BunPlugin = .{},
+
bundle_namespace_ref: ?Ref = null,
+
prepend_part: ?Part = null,
// These are used when bundling. They are filled in during the parser pass
@@ -4520,6 +4523,7 @@ pub const Part = struct {
cjs_imports,
react_fast_refresh,
dirname_filename,
+ bun_plugin,
};
pub const SymbolUseMap = std.ArrayHashMapUnmanaged(Ref, Symbol.Use, RefHashCtx, false);
@@ -4750,6 +4754,11 @@ pub fn printmem(comptime format: string, args: anytype) void {
Output.print(format, args);
}
+pub const BunPlugin = struct {
+ ref: Ref = Ref.None,
+ hoisted_stmts: std.ArrayListUnmanaged(Stmt) = .{},
+};
+
pub const Macro = struct {
const JavaScript = @import("javascript_core");
const JSCBase = @import("./bun.js/base.zig");
diff --git a/src/js_parser.zig b/src/js_parser.zig
index a69db9db4..164f915c4 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -2390,9 +2390,6 @@ pub const Parser = struct {
} else {
// When tree shaking is enabled, each top-level statement is potentially a separate part.
for (stmts) |stmt| {
- // switch (stmt.data) {
-
- // }
switch (stmt.data) {
.s_local => |local| {
if (local.decls.len > 1) {
@@ -3899,7 +3896,7 @@ fn NewParser_(
filename_ref: Ref = Ref.None,
dirname_ref: Ref = Ref.None,
import_meta_ref: Ref = Ref.None,
- promise_ref: ?Ref = null,
+ bun_plugin: js_ast.BunPlugin = .{},
scopes_in_order_visitor_index: usize = 0,
has_classic_runtime_warned: bool = false,
macro_call_count: MacroCallCountType = 0,
@@ -4334,13 +4331,12 @@ fn NewParser_(
parts.* = parts_;
}
+ const default_export_ref =
+ if (p.named_exports.get("default")) |default_| default_.ref else Ref.None;
+
while (parts_.len > 1) {
var parts_end: usize = 0;
var last_end = parts_.len;
- var default_export_ref = Ref.None;
- if (p.named_exports.get("default")) |named| {
- default_export_ref = named.ref;
- }
for (parts_) |part| {
const is_dead = part.can_be_removed_if_unused and can_remove_part: {
@@ -6297,6 +6293,38 @@ fn NewParser_(
return p.s(S.Empty{}, loc);
}
+ if (p.options.features.hoist_bun_plugin and strings.eqlComptime(path.text, "bun")) {
+ var plugin_i: usize = std.math.maxInt(usize);
+ const items = stmt.items;
+ for (items) |item, i| {
+ // Mark Bun.plugin()
+ // TODO: remove if they have multiple imports of the same name?
+ if (strings.eqlComptime(item.alias, "plugin")) {
+ const name = p.loadNameFromRef(item.name.ref.?);
+ const ref = try p.declareSymbol(.other, item.name.loc, name);
+ try p.is_import_item.put(p.allocator, ref, .{});
+ p.bun_plugin.ref = ref;
+ plugin_i = i;
+ break;
+ }
+ }
+
+ if (plugin_i != std.math.maxInt(usize)) {
+ var list = std.ArrayListUnmanaged(@TypeOf(stmt.items[0])){
+ .items = stmt.items,
+ .capacity = stmt.items.len,
+ };
+ // remove it from the list
+ _ = list.swapRemove(plugin_i);
+ stmt.items = list.items;
+ }
+
+ // if the import statement is now empty, remove it completely
+ if (stmt.items.len == 0 and stmt.default_name == null and stmt.star_name_loc == null) {
+ return p.s(S.Empty{}, loc);
+ }
+ }
+
const macro_remap = if ((comptime allow_macros) and !is_macro)
p.options.macro_context.getRemap(path.text)
else
@@ -12409,6 +12437,13 @@ fn NewParser_(
const allocator = p.allocator;
var opts = PrependTempRefsOpts{};
var partStmts = ListManaged(Stmt).fromOwnedSlice(allocator, stmts);
+
+ //
+ const bun_plugin_usage_count_before: usize = if (p.options.features.hoist_bun_plugin and !p.bun_plugin.ref.isNull())
+ p.symbols.items[p.bun_plugin.ref.innerIndex()].use_count_estimate
+ else
+ 0;
+
try p.visitStmtsAndPrependTempRefs(&partStmts, &opts);
// Insert any relocated variable statements now
@@ -12449,6 +12484,22 @@ fn NewParser_(
if (partStmts.items.len > 0) {
const _stmts = partStmts.toOwnedSlice();
+ // -- hoist_bun_plugin --
+ if (_stmts.len == 1 and p.options.features.hoist_bun_plugin and !p.bun_plugin.ref.isNull()) {
+ const bun_plugin_usage_count_after: usize = p.symbols.items[p.bun_plugin.ref.innerIndex()].use_count_estimate;
+ if (bun_plugin_usage_count_after > bun_plugin_usage_count_before) {
+ // Single-statement part which uses Bun.plugin()
+ // It's effectively an unrelated file
+ if (p.declared_symbols.items.len > 0 or p.symbol_uses.count() > 0) {
+ p.clearSymbolUsagesFromDeadPart(.{ .stmts = undefined, .declared_symbols = p.declared_symbols.items, .symbol_uses = p.symbol_uses });
+ }
+
+ p.bun_plugin.hoisted_stmts.append(p.allocator, _stmts[0]) catch unreachable;
+ return;
+ }
+ }
+ // -- hoist_bun_plugin --
+
try parts.append(js_ast.Part{
.stmts = _stmts,
.symbol_uses = p.symbol_uses,
@@ -18730,6 +18781,7 @@ fn NewParser_(
else
false,
// .top_Level_await_keyword = p.top_level_await_keyword,
+ .bun_plugin = p.bun_plugin,
};
}
diff --git a/src/runtime.zig b/src/runtime.zig
index bf589c54f..1a490e4d2 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -322,6 +322,8 @@ pub const Runtime = struct {
replace_exports: ReplaceableExport.Map = .{},
+ hoist_bun_plugin: bool = false,
+
pub const ReplaceableExport = union(enum) {
delete: void,
replace: JSAst.Expr,
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 48d591eff..acf9d057c 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -324,6 +324,43 @@ pub fn copyLowercase(in: string, out: []u8) string {
return out[0..in.len];
}
+pub fn copyLowercaseIfNeeded(in: string, out: []u8) string {
+ var in_slice: string = in;
+ var out_slice: []u8 = out[0..in.len];
+ var any = false;
+
+ begin: while (out_slice.len > 0) {
+ for (in_slice) |c, i| {
+ switch (c) {
+ 'A'...'Z' => {
+ @memcpy(out_slice.ptr, in_slice.ptr, i);
+ out_slice[i] = std.ascii.toLower(c);
+ const end = i + 1;
+ if (end >= out_slice.len) break :begin;
+ in_slice = in_slice[end..];
+ out_slice = out_slice[end..];
+ any = true;
+ continue :begin;
+ },
+ else => {},
+ }
+ }
+
+ if (!any) {
+ return in;
+ }
+
+ @memcpy(out_slice.ptr, in_slice.ptr, in_slice.len);
+ break :begin;
+ }
+
+ if (!any) {
+ return in;
+ }
+
+ return out[0..in.len];
+}
+
test "indexOf" {
const fixtures = .{
.{
diff --git a/test/bun.js/bun-loader-svelte/bun-loader-svelte.ts b/test/bun.js/bun-loader-svelte/bun-loader-svelte.ts
new file mode 100644
index 000000000..f0a6e3419
--- /dev/null
+++ b/test/bun.js/bun-loader-svelte/bun-loader-svelte.ts
@@ -0,0 +1,18 @@
+import { plugin } from "bun";
+
+await plugin({
+ name: "svelte loader",
+ async setup(builder) {
+ var { compile } = await import("svelte/compiler");
+ var { readFileSync } = await import("fs");
+ await 2;
+ builder.onLoad({ filter: /\.svelte$/ }, ({ path }) => ({
+ contents: compile(readFileSync(path, "utf8"), {
+ filename: path,
+ generate: "ssr",
+ }).js.code,
+ loader: "js",
+ }));
+ await 1;
+ },
+});
diff --git a/test/bun.js/bun-loader-svelte/package.json b/test/bun.js/bun-loader-svelte/package.json
new file mode 100644
index 000000000..ae4958ccc
--- /dev/null
+++ b/test/bun.js/bun-loader-svelte/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "bun-loader-svelte",
+ "module": "./bun-loader-svelte.ts"
+}
diff --git a/test/bun.js/plugins.test.ts b/test/bun.js/plugins.test.ts
index 743790cac..6593b077d 100644
--- a/test/bun.js/plugins.test.ts
+++ b/test/bun.js/plugins.test.ts
@@ -1,7 +1,8 @@
-import { it, expect, describe, afterAll } from "bun:test";
+import { plugin } from "bun";
+import { describe, expect, it } from "bun:test";
import { resolve } from "path";
-Bun.plugin({
+plugin({
name: "boop beep beep",
setup(builder) {
builder.onResolve({ filter: /boop/, namespace: "beep" }, () => ({
@@ -16,68 +17,133 @@ Bun.plugin({
},
});
-var objectModuleResult = {
- hello: "world",
-};
-Bun.plugin({
+plugin({
name: "an object module",
setup(builder) {
+ globalThis.objectModuleResult ||= {
+ hello: "world",
+ };
builder.onResolve({ filter: /.*/, namespace: "obj" }, ({ path }) => ({
path,
namespace: "obj",
}));
builder.onLoad({ filter: /.*/, namespace: "obj" }, () => ({
- exports: objectModuleResult,
+ exports: globalThis.objectModuleResult,
loader: "object",
}));
},
});
-Bun.plugin({
- name: "svelte loader",
- setup(builder) {
- var { compile } = require("svelte/compiler");
- var { readFileSync } = require("fs");
- builder.onLoad({ filter: /\.svelte$/ }, ({ path }) => ({
- contents: compile(readFileSync(path, "utf8"), {
- filename: path,
- generate: "ssr",
- }).js.code,
- loader: "js",
- }));
- },
-});
-
-var failingObject;
-Bun.plugin({
+plugin({
name: "failing loader",
setup(builder) {
+ globalThis.failingObject ||= {};
builder.onResolve({ filter: /.*/, namespace: "fail" }, ({ path }) => ({
path,
namespace: "fail",
}));
- builder.onLoad({ filter: /.*/, namespace: "fail" }, () => failingObject);
+ builder.onLoad(
+ { filter: /.*/, namespace: "fail" },
+ () => globalThis.failingObject
+ );
},
});
-var laterCode = "";
-
-Bun.plugin({
+plugin({
name: "delayed loader",
setup(builder) {
+ globalThis.laterCode = "";
+
builder.onResolve({ filter: /.*/, namespace: "delay" }, ({ path }) => ({
namespace: "delay",
path,
}));
builder.onLoad({ filter: /.*/, namespace: "delay" }, ({ path }) => ({
- contents: laterCode,
+ contents: (globalThis.laterCode ||= ""),
loader: "js",
}));
},
});
+plugin({
+ name: "async onLoad",
+ setup(builder) {
+ globalThis.asyncOnLoad = "";
+
+ builder.onResolve({ filter: /.*/, namespace: "async" }, ({ path }) => ({
+ namespace: "async",
+ path,
+ }));
+
+ builder.onLoad({ filter: /.*/, namespace: "async" }, async ({ path }) => {
+ await Promise.resolve(1);
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ contents: (globalThis.asyncOnLoad ||= ""),
+ loader: "js",
+ });
+ }, 1);
+ });
+ });
+
+ builder.onResolve({ filter: /.*/, namespace: "async-obj" }, ({ path }) => ({
+ namespace: "async-obj",
+ path,
+ }));
+ globalThis.asyncObject = {};
+ builder.onLoad(
+ { filter: /.*/, namespace: "async-obj" },
+ async ({ path }) => {
+ await Promise.resolve(1);
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ exports: (globalThis.asyncObject ||= {}),
+ loader: "object",
+ });
+ }, 1);
+ });
+ }
+ );
+
+ builder.onResolve({ filter: /.*/, namespace: "asyncfail" }, ({ path }) => ({
+ namespace: "asyncfail",
+ path,
+ }));
+
+ globalThis.asyncfail = false;
+ builder.onLoad(
+ { filter: /.*/, namespace: "asyncfail" },
+ async ({ path }) => {
+ await Promise.resolve(1);
+ await 1;
+ throw globalThis.asyncfail;
+ }
+ );
+
+ builder.onResolve({ filter: /.*/, namespace: "asyncret" }, ({ path }) => ({
+ namespace: "asyncret",
+ path,
+ }));
+
+ globalThis.asyncret = 123;
+ builder.onLoad(
+ { filter: /.*/, namespace: "asyncret" },
+ async ({ path }) => {
+ await 100;
+ await Promise.resolve(10);
+ return await globalThis.asyncret;
+ }
+ );
+ },
+});
+
+// This is to test that it works when imported from a separate file
+import "bun-loader-svelte";
+
describe("require", () => {
it("SSRs `<h1>Hello world!</h1>` with Svelte", () => {
const { default: App } = require("./hello.svelte");
@@ -114,6 +180,19 @@ describe("dynamic import", () => {
const result = await import("beep:boop");
expect(result.default).toBe(42);
});
+
+ it("async:onLoad returns 42", async () => {
+ globalThis.asyncOnLoad = "export default 42;";
+ const result = await import("async:hello42");
+ expect(result.default).toBe(42);
+ });
+
+ it("async object loader returns 42", async () => {
+ globalThis.asyncObject = { foo: 42, default: 43 };
+ const result = await import("async-obj:hello42");
+ expect(result.foo).toBe(42);
+ expect(result.default).toBe(43);
+ });
});
describe("import statement", () => {
@@ -141,7 +220,7 @@ describe("errors", () => {
for (let i = 0; i < validLoaders.length; i++) {
const loader = validLoaders[i];
const input = inputs[i];
- failingObject = { contents: input, loader };
+ globalThis.failingObject = { contents: input, loader };
expect(require(`fail:my-file-${loader}`).default).toBe("hi");
}
});
@@ -157,7 +236,7 @@ describe("errors", () => {
for (let i = 0; i < invalidLoaders.length; i++) {
const loader = invalidLoaders[i];
const input = inputs[i];
- failingObject = { contents: input, loader };
+ globalThis.failingObject = { contents: input, loader };
try {
require(`fail:my-file-${loader}`);
throw -1;
@@ -176,7 +255,7 @@ describe("errors", () => {
for (let i = 0; i < invalidLoaders.length; i++) {
const loader = invalidLoaders[i];
const input = inputs[i];
- failingObject = { contents: input, loader };
+ globalThis.failingObject = { contents: input, loader };
try {
require(`fail:my-file-${loader}-3`);
throw -1;
@@ -189,6 +268,33 @@ describe("errors", () => {
}
});
+ it("invalid async return value", async () => {
+ try {
+ globalThis.asyncret = { wat: true };
+ await import("asyncret:my-file");
+ throw -1;
+ } catch (e) {
+ if (e === -1) {
+ throw new Error("Expected error");
+ }
+
+ expect(e.message.length > 0).toBe(true);
+ }
+ });
+
+ it("async errors work", async () => {
+ try {
+ globalThis.asyncfail = new Error("async error");
+ await import("asyncfail:my-file");
+ throw -1;
+ } catch (e) {
+ if (e === -1) {
+ throw new Error("Expected error");
+ }
+ expect(e.message.length > 0).toBe(true);
+ }
+ });
+
it("invalid onLoad objects throw", () => {
const invalidOnLoadObjects = [
{},
@@ -197,7 +303,7 @@ describe("errors", () => {
{ contents: "", loader: "klz", resolveDir: -1 },
];
for (let i = 0; i < invalidOnLoadObjects.length; i++) {
- failingObject = invalidOnLoadObjects[i];
+ globalThis.failingObject = invalidOnLoadObjects[i];
try {
require(`fail:my-file-${i}-2`);
throw -1;
@@ -209,4 +315,17 @@ describe("errors", () => {
}
}
});
+
+ it("async transpiler errors work", async () => {
+ try {
+ globalThis.asyncOnLoad = `const x: string = -NaNAn../!!;`;
+ await import("async:fail");
+ throw -1;
+ } catch (e) {
+ if (e === -1) {
+ throw new Error("Expected error");
+ }
+ expect(e.message.length > 0).toBe(true);
+ }
+ });
});