aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--bench/module-loader/.gitignore5
-rw-r--r--bench/module-loader/create.js151
-rw-r--r--bench/module-loader/stub.js0
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp109
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h2
-rw-r--r--src/bun.js/builtins/BunBuiltinNames.h6
-rw-r--r--src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp122
-rw-r--r--src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h16
-rw-r--r--src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h82
-rw-r--r--src/bun.js/builtins/js/JSZigGlobalObject.js131
-rw-r--r--src/bun.js/javascript.zig77
-rw-r--r--src/bun.js/module.exports.js24
-rw-r--r--src/bundler.zig6
-rw-r--r--src/bundler/generate_node_modules_bundle.zig24
-rw-r--r--src/import_record.zig1
-rw-r--r--src/js_parser.zig158
-rw-r--r--src/js_printer.zig78
-rw-r--r--src/linker.zig97
-rw-r--r--src/options.zig2
-rw-r--r--src/resolver/resolve_path.zig2
-rw-r--r--src/runtime.js4
-rw-r--r--src/runtime.zig7
-rw-r--r--test/bun.js/import-meta.test.js42
-rw-r--r--test/bun.js/import.live.decl.js4
-rw-r--r--test/bun.js/import.live.rexport-require.js1
-rw-r--r--test/bun.js/import.live.rexport.js2
-rw-r--r--test/bun.js/require-js-top-level-await.js1
-rw-r--r--test/bun.js/require-js.js2
-rw-r--r--test/bun.js/require-js2.js1
30 files changed, 914 insertions, 249 deletions
diff --git a/Makefile b/Makefile
index 31cacb615..cc95d66ac 100644
--- a/Makefile
+++ b/Makefile
@@ -572,10 +572,10 @@ USOCKETS_DIR = $(BUN_DEPS_DIR)/uws/uSockets/
USOCKETS_SRC_DIR = $(BUN_DEPS_DIR)/uws/uSockets/src/
usockets:
- rm -rf $(BUN_DEPS_DIR)/uws/uSockets/*.o $(BUN_DEPS_DIR)/uws/uSockets/**/*.o $(BUN_DEPS_DIR)/uws/uSockets/*.a
+ rm -rf $(BUN_DEPS_DIR)/uws/uSockets/*.o $(BUN_DEPS_DIR)/uws/uSockets/**/*.o $(BUN_DEPS_DIR)/uws/uSockets/*.a $(BUN_DEPS_DIR)/uws/uSockets/*.bc
cd $(USOCKETS_DIR) && $(CC) $(EMIT_LLVM_FOR_RELEASE) $(MACOS_MIN_FLAG) -fPIC $(CFLAGS) $(UWS_CC_FLAGS) -save-temps -I$(BUN_DEPS_DIR)/uws/uSockets/src $(UWS_LDFLAGS) -g $(DEFAULT_LINKER_FLAGS) $(PLATFORM_LINKER_FLAGS) $(OPTIMIZATION_LEVEL) -g -c $(wildcard $(USOCKETS_SRC_DIR)/*.c) $(wildcard $(USOCKETS_SRC_DIR)/**/*.c)
cd $(USOCKETS_DIR) && $(CXX) $(EMIT_LLVM_FOR_RELEASE) $(MACOS_MIN_FLAG) -fPIC $(CXXFLAGS) $(UWS_CXX_FLAGS) -save-temps -I$(BUN_DEPS_DIR)/uws/uSockets/src $(UWS_LDFLAGS) -g $(DEFAULT_LINKER_FLAGS) $(PLATFORM_LINKER_FLAGS) $(OPTIMIZATION_LEVEL) -g -c $(wildcard $(USOCKETS_SRC_DIR)/*.cpp) $(wildcard $(USOCKETS_SRC_DIR)/**/*.cpp)
- cd $(USOCKETS_DIR) && $(AR) rcvs $(BUN_DEPS_OUT_DIR)/libusockets.a *.o *.ii *.bc *.i
+ cd $(USOCKETS_DIR) && $(AR) rcvs $(BUN_DEPS_OUT_DIR)/libusockets.a *.bc
uws: usockets
$(CXX) $(BITCODE_OR_SECTIONS) $(EMIT_LLVM_FOR_RELEASE) -fPIC -I$(BUN_DEPS_DIR)/uws/uSockets/src $(CLANG_FLAGS) $(CFLAGS) $(UWS_CXX_FLAGS) $(UWS_LDFLAGS) $(PLATFORM_LINKER_FLAGS) -c -I$(BUN_DEPS_DIR) $(BUN_DEPS_OUT_DIR)/libusockets.a $(BUN_DEPS_DIR)/libuwsockets.cpp -o $(BUN_DEPS_OUT_DIR)/libuwsockets.o
@@ -1212,7 +1212,7 @@ wasm-return1:
EMIT_LLVM_FOR_RELEASE=-emit-llvm -flto="full"
EMIT_LLVM_FOR_DEBUG=
-EMIT_LLVM=$(EMIT_LLVM_FOR_RELEASE)
+EMIT_LLVM=$(EMIT_LLVM_FOR_DEBUG)
# We do this outside of build.zig for performance reasons
# The C compilation stuff with build.zig is really slow and we don't need to run this as often as the rest
diff --git a/bench/module-loader/.gitignore b/bench/module-loader/.gitignore
new file mode 100644
index 000000000..e479d79cf
--- /dev/null
+++ b/bench/module-loader/.gitignore
@@ -0,0 +1,5 @@
+output
+import.mjs
+require.js
+meta.*
+*.mjs
diff --git a/bench/module-loader/create.js b/bench/module-loader/create.js
new file mode 100644
index 000000000..26ae6cfd3
--- /dev/null
+++ b/bench/module-loader/create.js
@@ -0,0 +1,151 @@
+const fs = require("fs");
+
+var count = 500;
+
+var saveStack = process.argv.includes("--save-stack") || false;
+var output = process.cwd() + "/output";
+// fs.rmdirSync("output", { recursive: true });
+try {
+ fs.mkdirSync(output, { recursive: true });
+} catch (e) {}
+
+for (var i = 0; i < count; i++) {
+ var file = output + "/file" + i + ".mjs";
+ fs.writeFileSync(
+ file,
+ new Array(Math.trunc(i * 0.25))
+ .fill("")
+ .map((k, j) => `export * from "./file${j}.mjs";`)
+ .join(";globalThis.exportCounter++;\n") +
+ `
+export * from "./file${i + 1}.mjs";
+export const hello${i} = "hello${i}";
+${saveStack ? `globalThis.evaluationOrder.push("${file}");` : ""}
+globalThis.counter++;
+`,
+ "utf8"
+ );
+ var file2 = output + "/file" + i + ".js";
+
+ fs.writeFileSync(
+ file2,
+ new Array(Math.trunc(i * 0.25))
+ .fill("")
+ .map((k, j) => `Object.assign(module.exports, require("./file${j}.js"));`)
+ .join(";globalThis.exportCounter++;\n") +
+ `
+ Object.assign(module.exports, require("./file${i + 1}.js"));
+module.exports.hello${i} = "hello${i}";
+${saveStack ? `globalThis.evaluationOrder.push("${file2}");` : ""}
+globalThis.counter++;
+`,
+ "utf8"
+ );
+}
+
+fs.writeFileSync(
+ output + `/file${count}.mjs`,
+ `
+ export const THE_END = true;
+ ${
+ saveStack
+ ? `globalThis.evaluationOrder.push("${output}/file${count}.mjs");`
+ : ""
+ }
+`,
+ "utf8"
+);
+
+fs.writeFileSync(
+ output + `/file${count}.js`,
+ `
+ module.exports.THE_END = true;
+ ${
+ saveStack
+ ? `globalThis.evaluationOrder.push("${output}/file${count}.js");`
+ : ""
+ }
+ `,
+ "utf8"
+);
+
+fs.writeFileSync(
+ import.meta.dir + "/import.mjs",
+ `${saveStack ? `globalThis.evaluationOrder = [];` : ""}
+ globalThis.counter=0; globalThis.exportCounter = 0;
+ console.time("import");
+ const Foo = await import('${output}/file0.mjs');
+ export const THE_END = Foo.THE_END;
+ console.timeEnd("import");
+ ${saveStack ? `console.log(globalThis.evaluationOrder.join("\\n"));` : ""}
+ console.log("Loaded", globalThis.counter, "files", "totaling", new Intl.NumberFormat().format(globalThis.exportCounter), 'exports');`,
+ "utf8"
+);
+
+fs.writeFileSync(
+ "meta.require.mjs",
+ `${saveStack ? `globalThis.evaluationOrder = [];` : ""}
+ globalThis.counter=0; globalThis.exportCounter = 0;
+console.time("import.meta.require");
+const Foo = import.meta.require("${output}/file0.mjs");
+export const THE_END = Foo.THE_END;
+console.timeEnd("import.meta.require");
+${saveStack ? `console.log(globalThis.evaluationOrder.join("\\n"));` : ""}
+console.log("Loaded", globalThis.counter, "files", "totaling", new Intl.NumberFormat().format(globalThis.exportCounter), 'exports');`,
+ "utf8"
+);
+
+fs.writeFileSync(
+ "meta.require.cjs",
+ `${saveStack ? `globalThis.evaluationOrder = [];` : ""}
+ globalThis.counter=0; globalThis.exportCounter = 0;
+ await 1;
+ console.time("import.meta.require");
+ const Foo = import.meta.require("${output}/file0.js");
+ export const THE_END = Foo.THE_END;
+ console.timeEnd("import.meta.require");
+ ${saveStack ? `console.log(globalThis.evaluationOrder.join("\\n"));` : ""}
+ console.log("Loaded", globalThis.counter, "files", "totaling", new Intl.NumberFormat().format(globalThis.exportCounter), 'exports');`,
+ "utf8"
+);
+
+fs.writeFileSync(
+ import.meta.dir + "/require.js",
+ `${saveStack ? `globalThis.evaluationOrder = [];` : ""}
+ globalThis.counter=0; globalThis.exportCounter = 0;
+ console.time("require");
+ const Foo = require("${output}/file0.js");
+ module.exports.THE_END = Foo.THE_END;
+ console.timeEnd("require");
+ ${saveStack ? `console.log(globalThis.evaluationOrder.join("\\n"));` : ""}
+ console.log("Loaded", globalThis.counter, "files", "totaling", new Intl.NumberFormat().format(globalThis.exportCounter), 'exports');
+ `,
+ "utf8"
+);
+
+console.log(`
+Created ${count} files in ${output}
+
+${
+ saveStack
+ ? "The evaluation order will be dumped to stdout"
+ : "To dump the evaluation order, run: \n bun run create.js -- --save-stack"
+}
+
+Run:
+
+ bun ./meta.require.mjs
+ bun ./meta.require.js
+
+Run:
+
+ bun ./import.mjs
+ node ./import.mjs
+ deno run -A ./import.mjs
+
+Run:
+
+ bun ./require.js
+ node ./require.js
+
+`);
diff --git a/bench/module-loader/stub.js b/bench/module-loader/stub.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/bench/module-loader/stub.js
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index e99f9a1cc..a51bc433b 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -328,13 +328,13 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure)
: JSC::JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
+ , m_bunVM(Bun__getVM())
, m_constructors(makeUnique<WebCore::DOMConstructors>())
, m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal))
, m_worldIsNormal(true)
, m_builtinInternalFunctions(vm)
{
- m_bunVM = Bun__getVM();
m_scriptExecutionContext = new WebCore::ScriptExecutionContext(&vm, this);
}
@@ -379,6 +379,8 @@ void GlobalObject::setConsole(void* console)
#pragma mark - Globals
+static JSC_DECLARE_HOST_FUNCTION(functionFulfillModuleSync);
+
JSC_DECLARE_CUSTOM_GETTER(functionLazyLoadStreamProtoypeMap_getter);
JSC_DEFINE_CUSTOM_GETTER(functionLazyLoadStreamProtoypeMap_getter,
@@ -1714,7 +1716,38 @@ void GlobalObject::finishCreation(VM& vm)
auto* prototype = createJSSinkControllerPrototype(init.vm, init.owner, WebCore::SinkID::ArrayBufferSink);
init.set(prototype);
});
+ m_importMetaObjectStructure.initLater(
+ [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSObject>::Initializer& init) {
+ JSC::JSObject* metaProperties = JSC::constructEmptyObject(init.owner, init.owner->objectPrototype(), 10);
+ auto& vm = init.vm;
+ auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(init.owner);
+ auto clientData = WebCore::clientData(vm);
+ metaProperties->putDirect(vm, clientData->builtinNames().filePublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().dirPublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), jsEmptyString(vm), 0);
+ metaProperties->putDirect(vm, clientData->builtinNames().mainPublicName(), jsBoolean(false), 0);
+ metaProperties->putDirectBuiltinFunction(vm, globalObject,
+ clientData->builtinNames().requirePublicName(), jsZigGlobalObjectRequireCodeGenerator(vm), 0);
+ metaProperties->putDirectBuiltinFunction(vm, globalObject,
+ clientData->builtinNames().loadModulePublicName(), jsZigGlobalObjectLoadModuleCodeGenerator(vm), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
+ metaProperties->putDirectBuiltinFunction(vm, globalObject,
+ clientData->builtinNames().requireModulePublicName(), jsZigGlobalObjectRequireModuleCodeGenerator(vm), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
+
+ metaProperties->putDirectNativeFunction(vm, globalObject, clientData->builtinNames().resolvePublicName(), 1,
+ functionImportMeta__resolve,
+ NoIntrinsic,
+ JSC::PropertyAttribute::Function | 0);
+ metaProperties->putDirectNativeFunction(
+ vm, globalObject, clientData->builtinNames().resolveSyncPublicName(),
+ 1,
+ functionImportMeta__resolveSync,
+ NoIntrinsic,
+ JSC::PropertyAttribute::Function | 0);
+
+ init.set(metaProperties);
+ });
m_lazyReadableStreamPrototypeMap.initLater(
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSMap>::Initializer& init) {
auto* map = JSC::JSMap::create(init.owner, init.vm, init.owner->mapStructure());
@@ -1751,7 +1784,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
auto& builtinNames = WebCore::builtinNames(vm);
WTF::Vector<GlobalPropertyInfo> extraStaticGlobals;
- extraStaticGlobals.reserveCapacity(29);
+ extraStaticGlobals.reserveCapacity(30);
JSC::Identifier queueMicrotaskIdentifier = JSC::Identifier::fromString(vm, "queueMicrotask"_s);
extraStaticGlobals.uncheckedAppend(
@@ -1844,7 +1877,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.isAbortSignalPrivateName(), JSFunction::create(vm, this, 1, String(), isAbortSignal), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.getInternalWritableStreamPrivateName(), JSFunction::create(vm, this, 1, String(), getInternalWritableStream), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.createWritableStreamFromInternalPrivateName(), JSFunction::create(vm, this, 1, String(), createWritableStreamFromInternal), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
-
+ extraStaticGlobals.uncheckedAppend(GlobalPropertyInfo(builtinNames.fulfillModuleSyncPrivateName(), JSFunction::create(vm, this, 1, String(), functionFulfillModuleSync), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::Function));
this->addStaticGlobals(extraStaticGlobals.data(), extraStaticGlobals.size());
extraStaticGlobals.releaseBuffer();
@@ -1856,6 +1889,9 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
putDirectBuiltinFunction(vm, this, builtinNames.readableStreamToArrayPrivateName(), readableStreamReadableStreamToArrayCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
putDirectBuiltinFunction(vm, this, builtinNames.assignDirectStreamPrivateName(), readableStreamInternalsAssignDirectStreamCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ // putDirectBuiltinFunction(vm, this, builtinNames.loadModulePrivateName(), jsZigGlobalObjectInternalsLoadModuleCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ // putDirectBuiltinFunction(vm, this, builtinNames.requireModulePrivateName(), jsZigGlobalObjectInternalsRequireModuleCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+
putDirectNativeFunction(vm, this, builtinNames.createUninitializedArrayBufferPrivateName(), 1, functionCreateUninitializedArrayBuffer, NoIntrinsic, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::Function);
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "process"_s), JSC::CustomGetterSetter::create(vm, property_lazyProcessGetter, property_lazyProcessSetter),
@@ -2096,6 +2132,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_JSFFIFunctionStructure.visit(visitor);
thisObject->m_JSArrayBufferSinkClassStructure.visit(visitor);
thisObject->m_JSArrayBufferControllerPrototype.visit(visitor);
+ thisObject->m_importMetaObjectStructure.visit(visitor);
thisObject->m_lazyReadableStreamPrototypeMap.visit(visitor);
visitor.append(thisObject->m_readableStreamToArrayBufferResolve);
@@ -2200,6 +2237,40 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* g
return result;
}
+static JSC_DEFINE_HOST_FUNCTION(functionFulfillModuleSync,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSC::JSValue key = callFrame->argument(0);
+
+ auto moduleKey = key.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(scope, JSValue::encode(JSC::jsUndefined()));
+
+ if (moduleKey.endsWith(".node"_s)) {
+ throwException(globalObject, scope, createTypeError(globalObject, "To load Node-API modules, use require() or process.dlopen instead of importSync."_s));
+ return JSValue::encode(JSC::jsUndefined());
+ }
+
+ auto specifier = Zig::toZigString(moduleKey);
+ ErrorableResolvedSource res;
+ res.success = false;
+ res.result.err.code = 0;
+ res.result.err.ptr = nullptr;
+
+ Zig__GlobalObject__fetch(&res, globalObject, &specifier, &specifier);
+
+ if (!res.success) {
+ throwException(scope, res.result.err, globalObject);
+ return JSValue::encode(JSC::jsUndefined());
+ }
+
+ auto provider = Zig::SourceProvider::create(res.result.value);
+ globalObject->moduleLoader()->provideFetch(globalObject, key, JSC::SourceCode(provider));
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
+ RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
+}
+
JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject,
JSModuleLoader* loader, JSValue key,
JSValue value1, JSValue value2)
@@ -2275,14 +2346,16 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
- JSC::JSObject* metaProperties = JSC::constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure());
+ JSC::JSObject* metaProperties = JSC::constructEmptyObject(globalObject, reinterpret_cast<GlobalObject*>(globalObject)->ImportMetaObjectPrototype());
RETURN_IF_EXCEPTION(scope, nullptr);
auto clientData = WebCore::clientData(vm);
JSString* keyString = key.toStringOrNull(globalObject);
if (UNLIKELY(!keyString)) {
- return metaProperties;
+ RELEASE_AND_RETURN(scope, metaProperties);
}
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
auto view = keyString->value(globalObject);
auto index = view.reverseFind('/', view.length());
if (index != WTF::notFound) {
@@ -2293,28 +2366,16 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
JSC::jsSubstring(globalObject, keyString, index + 1, keyString->length() - index - 1));
} else {
metaProperties->putDirect(vm, clientData->builtinNames().filePublicName(), keyString);
- metaProperties->putDirect(vm, clientData->builtinNames().dirPublicName(), jsEmptyString(vm));
}
- metaProperties->putDirect(vm, clientData->builtinNames().resolvePublicName(),
- JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject), 0,
- clientData->builtinNames().resolvePublicName().string(), functionImportMeta__resolve),
- JSC::PropertyAttribute::Function | 0);
- metaProperties->putDirect(vm, clientData->builtinNames().resolveSyncPublicName(),
- JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject), 0,
- clientData->builtinNames().resolveSyncPublicName().string(), functionImportMeta__resolveSync),
- JSC::PropertyAttribute::Function | 0);
-
- metaProperties->putDirectBuiltinFunction(vm, globalObject, clientData->builtinNames().requirePublicName(),
- jsZigGlobalObjectRequireCodeGenerator(vm),
- JSC::PropertyAttribute::Builtin | 0);
-
- metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), key);
- metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), JSC::JSValue(JSC::jsString(vm, WTF::URL::fileURLWithFileSystemPath(view).string())));
-
- RETURN_IF_EXCEPTION(scope, nullptr);
+ metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), keyString);
+ if (view.startsWith('/')) {
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), JSC::JSValue(JSC::jsString(vm, WTF::URL::fileURLWithFileSystemPath(view).string())));
+ } else {
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), keyString);
+ }
- return metaProperties;
+ RELEASE_AND_RETURN(scope, metaProperties);
}
JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* globalObject,
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 41556e639..cabaf57a9 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -153,6 +153,7 @@ public:
WebCore::JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; }
JSC::Structure* FFIFunctionStructure() { return m_JSFFIFunctionStructure.getInitializedOnMainThread(this); }
JSC::Structure* NapiClassStructure() { return m_NapiClassStructure.getInitializedOnMainThread(this); }
+ JSC::JSObject* ImportMetaObjectPrototype() { return m_importMetaObjectStructure.getInitializedOnMainThread(this); }
JSC::Structure* ArrayBufferSinkStructure() { return m_JSArrayBufferSinkClassStructure.getInitializedOnMainThread(this); }
JSC::JSObject* ArrayBufferSink() { return m_JSArrayBufferSinkClassStructure.constructorInitializedOnMainThread(this); }
JSC::JSValue ArrayBufferSinkPrototype() { return m_JSArrayBufferSinkClassStructure.prototypeInitializedOnMainThread(this); }
@@ -183,6 +184,7 @@ private:
LazyClassStructure m_NapiClassStructure;
LazyClassStructure m_JSArrayBufferSinkClassStructure;
LazyProperty<JSGlobalObject, JSObject> m_JSArrayBufferControllerPrototype;
+ LazyProperty<JSGlobalObject, JSObject> m_importMetaObjectStructure;
LazyProperty<JSGlobalObject, JSMap> m_lazyReadableStreamPrototypeMap;
DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock);
diff --git a/src/bun.js/builtins/BunBuiltinNames.h b/src/bun.js/builtins/BunBuiltinNames.h
index 1b49338c0..6acd30a8b 100644
--- a/src/bun.js/builtins/BunBuiltinNames.h
+++ b/src/bun.js/builtins/BunBuiltinNames.h
@@ -101,6 +101,7 @@ using namespace JSC;
macro(flush) \
macro(flushAlgorithm) \
macro(format) \
+ macro(fulfillModuleSync) \
macro(get) \
macro(getInternalWritableStream) \
macro(handleEvent) \
@@ -125,9 +126,11 @@ using namespace JSC;
macro(join) \
macro(kind) \
macro(lazy) \
- macro(lazyStreamPrototypeMap) \
macro(lazyLoad) \
+ macro(lazyStreamPrototypeMap) \
+ macro(loadModule) \
macro(localStreams) \
+ macro(main) \
macro(makeDOMException) \
macro(makeGetterTypeError) \
macro(makeThisTypeError) \
@@ -174,6 +177,7 @@ using namespace JSC;
macro(releaseLock) \
macro(removeEventListener) \
macro(require) \
+ macro(requireModule) \
macro(resolve) \
macro(resolveSync) \
macro(resume) \
diff --git a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp
index 05863cc27..421ba1181 100644
--- a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp
+++ b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.cpp
@@ -49,7 +49,7 @@ namespace WebCore {
const JSC::ConstructAbility s_jsZigGlobalObjectRequireCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_jsZigGlobalObjectRequireCodeConstructorKind = JSC::ConstructorKind::None;
-const int s_jsZigGlobalObjectRequireCodeLength = 1225;
+const int s_jsZigGlobalObjectRequireCodeLength = 1221;
static const JSC::Intrinsic s_jsZigGlobalObjectRequireCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_jsZigGlobalObjectRequireCode =
"(function (name) {\n" \
@@ -57,7 +57,7 @@ const char* const s_jsZigGlobalObjectRequireCode =
" if (typeof name !== \"string\") {\n" \
" @throwTypeError(\"require() expects a string as its argument\");\n" \
" }\n" \
- "\n" \
+ " \n" \
" const resolved = this.resolveSync(name, this.path);\n" \
" var requireCache = (globalThis[Symbol.for(\"_requireCache\")] ||= new @Map);\n" \
" var cached = requireCache.@get(resolved);\n" \
@@ -69,6 +69,7 @@ const char* const s_jsZigGlobalObjectRequireCode =
" return cached;\n" \
" }\n" \
"\n" \
+ "\n" \
" //\n" \
" if (resolved.endsWith(\".json\")) {\n" \
" var fs = (globalThis[Symbol.for(\"_fs\")] ||= Bun.fs());\n" \
@@ -85,9 +86,124 @@ const char* const s_jsZigGlobalObjectRequireCode =
" var exports = Bun.TOML.parse(fs.readFileSync(resolved, \"utf8\"));\n" \
" requireCache.@set(resolved, exports);\n" \
" return exports;\n" \
+ " } else {\n" \
+ " var exports = this.requireModule(this, resolved);\n" \
+ " requireCache.@set(resolved, exports);\n" \
+ " return exports;\n" \
+ " }\n" \
+ "})\n" \
+;
+
+const JSC::ConstructAbility s_jsZigGlobalObjectLoadModuleCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_jsZigGlobalObjectLoadModuleCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_jsZigGlobalObjectLoadModuleCodeLength = 2783;
+static const JSC::Intrinsic s_jsZigGlobalObjectLoadModuleCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_jsZigGlobalObjectLoadModuleCode =
+ "(function (meta, resolvedSpecifier) {\n" \
+ " \"use strict\";\n" \
+ " var queue = @createFIFO();\n" \
+ " var key = resolvedSpecifier;\n" \
+ " \n" \
+ " var Loader = globalThis.Loader;\n" \
+ " var registry = Loader.registry;\n" \
+ " while (key) {\n" \
+ " @fulfillModuleSync(key);\n" \
+ " var entry = registry.@get(key);\n" \
+ "\n" \
+ " //\n" \
+ " //\n" \
+ " //\n" \
+ " //\n" \
+ " var sourceCodeObject = @getPromiseInternalField(entry.fetch, @promiseFieldReactionsOrResult);\n" \
+ " \n" \
+ "\n" \
+ " //\n" \
+ " //\n" \
+ " //\n" \
+ " var moduleRecordPromise = Loader.parseModule(key, sourceCodeObject);\n" \
+ " var module = entry.module;\n" \
+ " if (!module && moduleRecordPromise && @isPromise(moduleRecordPromise)) {\n" \
+ " var reactionsOrResult = @getPromiseInternalField(moduleRecordPromise, @promiseFieldReactionsOrResult);\n" \
+ " var flags = @getPromiseInternalField(moduleRecordPromise, @promiseFieldFlags);\n" \
+ " var state = flags & @promiseStateMask;\n" \
+ "\n" \
+ " //\n" \
+ " if (state === @promiseStatePending || (reactionsOrResult && @isPromise(reactionsOrResult))) {\n" \
+ " @throwTypeError(`require() async module \\\"${key}\\\" is unsupported`);\n" \
+ " \n" \
+ " } else if (state === @promiseStateRejected) {\n" \
+ " //\n" \
+ " //\n" \
+ " @throwTypeError(`${reactionsOrResult?.message ?? \"An error occurred\"} while parsing module \\\"${key}\\\"`);\n" \
+ " }\n" \
+ " entry.module = module = reactionsOrResult;\n" \
+ " } else if (moduleRecordPromise && !module) {\n" \
+ " entry.module = module = moduleRecordPromise;\n" \
+ " }\n" \
+ "\n" \
+ " //\n" \
+ " @setStateToMax(entry, @ModuleLink);\n" \
+ " var dependenciesMap = module.dependenciesMap;\n" \
+ " var requestedModules = Loader.requestedModules(module);\n" \
+ " var dependencies = @newArrayWithSize(requestedModules.length);\n" \
+ " \n" \
+ " for (var i = 0, length = requestedModules.length; i < length; ++i) {\n" \
+ " var depName = requestedModules[i];\n" \
+ "\n" \
+ " //\n" \
+ " //\n" \
+ " var depKey = depName[0] === '/' ? depName : Loader.resolveSync(depName, key, @undefined);\n" \
+ " var depEntry = Loader.ensureRegistered(depKey);\n" \
+ "\n" \
+ " if (depEntry.state < @ModuleLink) {\n" \
+ " queue.push(depKey);\n" \
+ " }\n" \
+ "\n" \
+ " @putByValDirect(dependencies, i, depEntry);\n" \
+ " dependenciesMap.@set(depName, depEntry);\n" \
+ " }\n" \
+ "\n" \
+ " entry.dependencies = dependencies;\n" \
+ " key = queue.shift();\n" \
+ " while (key && ((registry.@get(key)?.state ?? @ModuleFetch) >= @ModuleLink)) {\n" \
+ " key = queue.shift();\n" \
+ " }\n" \
+ " }\n" \
+ "\n" \
+ " var linkAndEvaluateResult = Loader.linkAndEvaluateModule(resolvedSpecifier, @undefined);\n" \
+ " if (linkAndEvaluateResult && @isPromise(linkAndEvaluateResult)) {\n" \
+ " //\n" \
+ " //\n" \
+ " @throwTypeError(`require() async module \\\"${resolvedSpecifier}\\\" is unsupported`);\n" \
+ " }\n" \
+ "\n" \
+ " return Loader.registry.@get(resolvedSpecifier);\n" \
+ "})\n" \
+;
+
+const JSC::ConstructAbility s_jsZigGlobalObjectRequireModuleCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_jsZigGlobalObjectRequireModuleCodeConstructorKind = JSC::ConstructorKind::None;
+const int s_jsZigGlobalObjectRequireModuleCodeLength = 613;
+static const JSC::Intrinsic s_jsZigGlobalObjectRequireModuleCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_jsZigGlobalObjectRequireModuleCode =
+ "(function (meta, resolved) {\n" \
+ " \"use strict\";\n" \
+ " var Loader = globalThis.Loader;\n" \
+ " var entry = Loader.registry.@get(resolved);\n" \
+ "\n" \
+ " if (!entry || !entry.evaluated) {\n" \
+ " entry = this.loadModule(meta, resolved); \n" \
" }\n" \
"\n" \
- " @throwTypeError(`Dynamic require isn't supported for file type: ${resolved.subsring(resolved.lastIndexOf(\".\") + 1) || resolved}`);\n" \
+ " if (!entry || !entry.evaluated || !entry.module) {\n" \
+ " @throwTypeError(`require() failed to evaluate module \\\"${resolved}\\\". This is an internal consistentency error.`);\n" \
+ " }\n" \
+ " var exports = Loader.getModuleNamespaceObject(entry.module);\n" \
+ " var commonJS = exports.default;\n" \
+ " if (commonJS && @isObject(commonJS) && Symbol.for(\"CommonJS\") in commonJS) {\n" \
+ " return commonJS();\n" \
+ " }\n" \
+ " return exports;\n" \
"})\n" \
;
diff --git a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h
index f02eec836..31092981d 100644
--- a/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h
+++ b/src/bun.js/builtins/cpp/JSZigGlobalObjectBuiltins.h
@@ -51,17 +51,33 @@ extern const char* const s_jsZigGlobalObjectRequireCode;
extern const int s_jsZigGlobalObjectRequireCodeLength;
extern const JSC::ConstructAbility s_jsZigGlobalObjectRequireCodeConstructAbility;
extern const JSC::ConstructorKind s_jsZigGlobalObjectRequireCodeConstructorKind;
+extern const char* const s_jsZigGlobalObjectLoadModuleCode;
+extern const int s_jsZigGlobalObjectLoadModuleCodeLength;
+extern const JSC::ConstructAbility s_jsZigGlobalObjectLoadModuleCodeConstructAbility;
+extern const JSC::ConstructorKind s_jsZigGlobalObjectLoadModuleCodeConstructorKind;
+extern const char* const s_jsZigGlobalObjectRequireModuleCode;
+extern const int s_jsZigGlobalObjectRequireModuleCodeLength;
+extern const JSC::ConstructAbility s_jsZigGlobalObjectRequireModuleCodeConstructAbility;
+extern const JSC::ConstructorKind s_jsZigGlobalObjectRequireModuleCodeConstructorKind;
#define WEBCORE_FOREACH_JSZIGGLOBALOBJECT_BUILTIN_DATA(macro) \
macro(require, jsZigGlobalObjectRequire, 1) \
+ macro(loadModule, jsZigGlobalObjectLoadModule, 2) \
+ macro(requireModule, jsZigGlobalObjectRequireModule, 2) \
#define WEBCORE_BUILTIN_JSZIGGLOBALOBJECT_REQUIRE 1
+#define WEBCORE_BUILTIN_JSZIGGLOBALOBJECT_LOADMODULE 1
+#define WEBCORE_BUILTIN_JSZIGGLOBALOBJECT_REQUIREMODULE 1
#define WEBCORE_FOREACH_JSZIGGLOBALOBJECT_BUILTIN_CODE(macro) \
macro(jsZigGlobalObjectRequireCode, require, ASCIILiteral(), s_jsZigGlobalObjectRequireCodeLength) \
+ macro(jsZigGlobalObjectLoadModuleCode, loadModule, ASCIILiteral(), s_jsZigGlobalObjectLoadModuleCodeLength) \
+ macro(jsZigGlobalObjectRequireModuleCode, requireModule, ASCIILiteral(), s_jsZigGlobalObjectRequireModuleCodeLength) \
#define WEBCORE_FOREACH_JSZIGGLOBALOBJECT_BUILTIN_FUNCTION_NAME(macro) \
+ macro(loadModule) \
macro(require) \
+ macro(requireModule) \
#define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
JSC::FunctionExecutable* codeName##Generator(JSC::VM&);
diff --git a/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h b/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h
index c52f65d85..fc5e2406a 100644
--- a/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h
+++ b/src/bun.js/builtins/cpp/WebCoreJSBuiltinInternals.h
@@ -1,5 +1,87 @@
//clang-format off
namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
+namespace Zig { class GlobalObject; }
/*
* Copyright (c) 2015 Igalia
* Copyright (c) 2015 Igalia S.L.
diff --git a/src/bun.js/builtins/js/JSZigGlobalObject.js b/src/bun.js/builtins/js/JSZigGlobalObject.js
index cb3446159..7b82067ec 100644
--- a/src/bun.js/builtins/js/JSZigGlobalObject.js
+++ b/src/bun.js/builtins/js/JSZigGlobalObject.js
@@ -28,7 +28,7 @@ function require(name) {
if (typeof name !== "string") {
@throwTypeError("require() expects a string as its argument");
}
-
+
const resolved = this.resolveSync(name, this.path);
var requireCache = (globalThis[Symbol.for("_requireCache")] ||= new @Map);
var cached = requireCache.@get(resolved);
@@ -40,6 +40,7 @@ function require(name) {
return cached;
}
+
// TODO: remove this hardcoding
if (resolved.endsWith(".json")) {
var fs = (globalThis[Symbol.for("_fs")] ||= Bun.fs());
@@ -56,7 +57,133 @@ function require(name) {
var exports = Bun.TOML.parse(fs.readFileSync(resolved, "utf8"));
requireCache.@set(resolved, exports);
return exports;
+ } else {
+ var exports = this.requireModule(this, resolved);
+ requireCache.@set(resolved, exports);
+ return exports;
+ }
+}
+
+function loadModule(meta, resolvedSpecifier) {
+ "use strict";
+ var Loader = globalThis.Loader;
+
+ var queue = @createFIFO();
+ var key = resolvedSpecifier;
+ var registry = Loader.registry;
+ while (key) {
+ @fulfillModuleSync(key);
+ var entry = registry.@get(key);
+
+ // entry.fetch is a Promise<SourceCode>
+ // SourceCode is not a string, it's a JSC::SourceCode object
+ // this pulls it out of the promise without delaying by a tick
+ // the promise is already fullfilled by @fullfillModuleSync
+ var sourceCodeObject = @getPromiseInternalField(
+ entry.fetch,
+ @promiseFieldReactionsOrResult
+ );
+
+ // parseModule() returns a Promise, but the value is already fulfilled
+ // so we just pull it out of the promise here once again
+ // But, this time we do it a little more carefully because this is a JSC function call and not bun source code
+ var moduleRecordPromise = Loader.parseModule(key, sourceCodeObject);
+ var module = entry.module;
+ if (!module && moduleRecordPromise && @isPromise(moduleRecordPromise)) {
+ var reactionsOrResult = @getPromiseInternalField(
+ moduleRecordPromise,
+ @promiseFieldReactionsOrResult
+ );
+ var flags = @getPromiseInternalField(
+ moduleRecordPromise,
+ @promiseFieldFlags
+ );
+ var state = flags & @promiseStateMask;
+
+ // this branch should never happen, but just to be safe
+ if (
+ state === @promiseStatePending ||
+ (reactionsOrResult && @isPromise(reactionsOrResult))
+ ) {
+ @throwTypeError(`require() async module \"${key}\" is unsupported`);
+ } else if (state === @promiseStateRejected) {
+ // this branch happens if there is a syntax error and somehow bun didn't catch it
+ // "throw" is unsupported here, so we use "throwTypeError" (TODO: use SyntaxError but preserve the specifier)
+ @throwTypeError(
+ `${
+ reactionsOrResult?.message ?? "An error occurred"
+ } while parsing module \"${key}\"`
+ );
+ }
+ entry.module = module = reactionsOrResult;
+ } else if (moduleRecordPromise && !module) {
+ entry.module = module = moduleRecordPromise;
+ }
+
+ // This is very similar to "requestInstantiate" in ModuleLoader.js in JavaScriptCore.
+ @setStateToMax(entry, @ModuleLink);
+ var dependenciesMap = module.dependenciesMap;
+ var requestedModules = Loader.requestedModules(module);
+ var dependencies = @newArrayWithSize(requestedModules.length);
+
+ for (var i = 0, length = requestedModules.length; i < length; ++i) {
+ var depName = requestedModules[i];
+
+ // optimization: if it starts with a slash then it's an absolute path
+ // we don't need to run the resolver a 2nd time
+ var depKey =
+ depName[0] === "/"
+ ? depName
+ : Loader.resolveSync(depName, key, @undefined);
+ var depEntry = Loader.ensureRegistered(depKey);
+
+ if (depEntry.state < @ModuleLink) {
+ queue.push(depKey);
+ }
+
+ @putByValDirect(dependencies, i, depEntry);
+ dependenciesMap.@set(depName, depEntry);
+ }
+
+ entry.dependencies = dependencies;
+ key = queue.shift();
+ while (key && (registry.@get(key)?.state ?? @ModuleFetch) >= @ModuleLink) {
+ key = queue.shift();
+ }
+ }
+
+ var linkAndEvaluateResult = Loader.linkAndEvaluateModule(
+ resolvedSpecifier,
+ @undefined
+ );
+ if (linkAndEvaluateResult && @isPromise(linkAndEvaluateResult)) {
+ // if you use top-level await, or any dependencies use top-level await, then we throw here
+ // this means the module will still actually load eventually, but that's okay.
+ @throwTypeError(
+ `require() async module \"${resolvedSpecifier}\" is unsupported`
+ );
+ }
+
+ return Loader.registry.@get(resolvedSpecifier);
+
+}
+
+function requireModule(meta, resolved) {
+ "use strict";
+ var Loader = globalThis.Loader;
+ var entry = Loader.registry.@get(resolved);
+
+ if (!entry || !entry.evaluated) {
+ entry = this.loadModule(meta, resolved);
}
- @throwTypeError(`Dynamic require isn't supported for file type: ${resolved.subsring(resolved.lastIndexOf(".") + 1) || resolved}`);
+ if (!entry || !entry.evaluated || !entry.module) {
+ @throwTypeError(`require() failed to evaluate module \"${resolved}\". This is an internal consistentency error.`);
+ }
+ var exports = Loader.getModuleNamespaceObject(entry.module);
+ var commonJS = exports.default;
+ if (commonJS && @isObject(commonJS) && Symbol.for("CommonJS") in commonJS) {
+ return commonJS();
+ }
+ return exports;
}
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index 937b40cc6..255d50e33 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -641,16 +641,25 @@ pub const VirtualMachine = struct {
const shared_library_suffix = if (Environment.isMac) "dylib" else if (Environment.isLinux) "so" else "";
- inline fn _fetch(
+ const FetchFlags = enum {
+ transpile,
+ print_source,
+ print_source_and_clone,
+
+ pub fn disableTranspiling(this: FetchFlags) bool {
+ return this != .transpile;
+ }
+ };
+ fn _fetch(
+ jsc_vm: *VirtualMachine,
_: *JSGlobalObject,
_specifier: string,
_: string,
log: *logger.Log,
- comptime disable_transpilying: bool,
+ comptime flags: FetchFlags,
) !ResolvedSource {
std.debug.assert(VirtualMachine.vm_loaded);
- var jsc_vm = vm;
-
+ const disable_transpilying = comptime flags.disableTranspiling();
if (jsc_vm.node_modules != null and strings.eqlComptime(_specifier, bun_file_import_path)) {
// We kind of need an abstraction around this.
// Basically we should subclass JSC::SourceCode with:
@@ -707,7 +716,8 @@ pub const VirtualMachine = struct {
jsx.parse = false;
var opts = js_parser.Parser.Options.init(jsx, .js);
opts.enable_bundling = false;
- opts.transform_require_to_import = true;
+ opts.transform_require_to_import = false;
+ opts.features.dynamic_require = true;
opts.can_import_from_bundle = bundler.options.node_modules_bundle != null;
opts.features.hot_module_reloading = false;
opts.features.react_fast_refresh = false;
@@ -1011,7 +1021,11 @@ pub const VirtualMachine = struct {
if (comptime disable_transpilying) {
return ResolvedSource{
.allocator = null,
- .source_code = ZigString.init(parse_result.source.contents),
+ .source_code = switch (comptime flags) {
+ .print_source_and_clone => ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable),
+ .print_source => ZigString.init(parse_result.source.contents),
+ else => unreachable,
+ },
.specifier = ZigString.init(specifier),
.source_url = ZigString.init(path.text),
.hash = 0,
@@ -1340,10 +1354,22 @@ pub const VirtualMachine = struct {
pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void {
var log = logger.Log.init(vm.bundler.allocator);
const spec = specifier.slice();
- const result = _fetch(global, spec, source.slice(), &log, false) catch |err| {
- processFetchLog(global, specifier, source, &log, ret, err);
- return;
- };
+ // threadlocal is cheaper in linux
+ var jsc_vm: *VirtualMachine = if (comptime Environment.isLinux)
+ vm
+ else
+ global.bunVM();
+
+ const result = if (!jsc_vm.bundler.options.disable_transpilation)
+ @call(.{ .modifier = .always_inline }, _fetch, .{ jsc_vm, global, spec, source.slice(), &log, .transpile }) catch |err| {
+ processFetchLog(global, specifier, source, &log, ret, err);
+ return;
+ }
+ else
+ _fetch(jsc_vm, global, spec, source.slice(), &log, .print_source_and_clone) catch |err| {
+ processFetchLog(global, specifier, source, &log, ret, err);
+ return;
+ };
if (log.errors > 0) {
processFetchLog(global, specifier, source, &log, ret, error.LinkError);
@@ -1494,22 +1520,28 @@ pub const VirtualMachine = struct {
}
pub fn loadEntryPoint(this: *VirtualMachine, entry_path: string) !*JSInternalPromise {
- try this.entry_point.generate(@TypeOf(this.bundler), &this.bundler, Fs.PathName.init(entry_path), main_file_name);
this.main = entry_path;
+ try this.entry_point.generate(@TypeOf(this.bundler), &this.bundler, Fs.PathName.init(entry_path), main_file_name);
var promise: *JSInternalPromise = undefined;
- // We first import the node_modules bundle. This prevents any potential TDZ issues.
- // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
- if (this.node_modules != null and !this.has_loaded_node_modules) {
- this.has_loaded_node_modules = true;
- promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(bun_file_import_path)));
- this.waitForPromise(promise);
- if (promise.status(this.global.vm()) == .Rejected)
- return promise;
- }
+ if (!this.bundler.options.disable_transpilation) {
- promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(main_file_name)));
+ // We first import the node_modules bundle. This prevents any potential TDZ issues.
+ // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
+ if (this.node_modules != null and !this.has_loaded_node_modules) {
+ this.has_loaded_node_modules = true;
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(bun_file_import_path)));
+
+ this.waitForPromise(promise);
+ if (promise.status(this.global.vm()) == .Rejected)
+ return promise;
+ }
+
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(main_file_name)));
+ } else {
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(this.main));
+ }
this.waitForPromise(promise);
@@ -1798,7 +1830,7 @@ pub const VirtualMachine = struct {
@maximum(top.position.column_start, 0),
)) |mapping| {
var log = logger.Log.init(default_allocator);
- var original_source = _fetch(this.global, top.source_url.slice(), "", &log, true) catch return;
+ var original_source = _fetch(this, this.global, top.source_url.slice(), "", &log, .print_source) catch return;
const code = original_source.source_code.slice();
top.position.line = mapping.original.lines;
top.position.line_start = mapping.original.lines;
@@ -2688,6 +2720,7 @@ pub const HardcodedModule = enum {
pub const LinkerMap = bun.ComptimeStringMap(
string,
.{
+ .{ "bun", "bun" },
.{ "bun:ffi", "bun:ffi" },
.{ "bun:jsc", "bun:jsc" },
.{ "bun:sqlite", "bun:sqlite" },
diff --git a/src/bun.js/module.exports.js b/src/bun.js/module.exports.js
index f0817a059..6873748e2 100644
--- a/src/bun.js/module.exports.js
+++ b/src/bun.js/module.exports.js
@@ -12,16 +12,6 @@ resolve.paths = () => [];
function require(pathString) {
// this refers to an ImportMeta instance
const resolved = this.resolveSync(pathString);
- if (
- !resolved.endsWith(".node") &&
- !resolved.endsWith(".json") &&
- !resolved.endsWith(".toml")
- ) {
- throw new Error(
- "Dynamic require() in Bun.js currently only supports .node, .json, and .toml files.\n\tConsider using ESM import() instead."
- );
- }
-
return this.require(resolved);
}
@@ -49,13 +39,13 @@ export function createRequire(filename) {
// but we don't support windows yet
process.platform !== "win32" ? "/" : "\\"
);
- var customImportMeta = {
- ...import.meta,
- path: filenameString,
- file:
- lastSlash > -1 ? filenameString.substring(lastSlash + 1) : filenameString,
- dir: lastSlash > -1 ? filenameString.substring(0, lastSlash) : "",
- };
+
+ var customImportMeta = Object.create(import.meta);
+ customImportMeta.path = filenameString;
+ customImportMeta.file =
+ lastSlash > -1 ? filenameString.substring(lastSlash + 1) : filenameString;
+ customImportMeta.dir =
+ lastSlash > -1 ? filenameString.substring(0, lastSlash) : "";
if (isURL) {
customImportMeta.url = filename;
diff --git a/src/bundler.zig b/src/bundler.zig
index c3f339436..c21162129 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -314,6 +314,10 @@ pub const Bundler = struct {
Analytics.is_ci = true;
}
+ if (strings.eqlComptime(this.env.map.get("BUN_DISABLE_TRANSPILER") orelse "0", "1")) {
+ this.options.disable_transpilation = true;
+ }
+
Analytics.disabled = Analytics.disabled or this.env.map.get("HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET") != null;
}
@@ -1122,7 +1126,7 @@ pub const Bundler = struct {
var opts = js_parser.Parser.Options.init(jsx, loader);
opts.enable_bundling = false;
- opts.transform_require_to_import = bundler.options.allow_runtime;
+ opts.transform_require_to_import = bundler.options.allow_runtime and !bundler.options.platform.isBun();
opts.features.allow_runtime = bundler.options.allow_runtime;
opts.features.trim_unused_imports = bundler.options.trim_unused_imports orelse loader.isTypeScript();
opts.features.should_fold_numeric_constants = platform.isBun();
diff --git a/src/bundler/generate_node_modules_bundle.zig b/src/bundler/generate_node_modules_bundle.zig
index d8f615144..a269944af 100644
--- a/src/bundler/generate_node_modules_bundle.zig
+++ b/src/bundler/generate_node_modules_bundle.zig
@@ -1295,6 +1295,8 @@ pub fn processFile(this: *GenerateNodeModuleBundle, worker: *ThreadPool.Worker,
var opts = js_parser.Parser.Options.init(jsx, loader);
opts.transform_require_to_import = false;
+ opts.features.dynamic_require = bundler.options.platform.isBun();
+
opts.enable_bundling = true;
opts.warn_about_unbundled_modules = false;
opts.macro_context = &worker.data.macro_context;
@@ -1321,9 +1323,20 @@ pub fn processFile(this: *GenerateNodeModuleBundle, worker: *ThreadPool.Worker,
if (bundler.options.platform.isBun()) {
if (JSC.DisabledModule.has(import_record.path.text)) {
import_record.path.is_disabled = true;
+ import_record.wrap_with_to_module = true;
import_record.is_bundled = true;
continue;
}
+
+ if (JSC.HardcodedModule.LinkerMap.get(import_record.path.text)) |remapped| {
+ import_record.path.text = remapped;
+ import_record.tag = if (strings.eqlComptime(remapped, "bun"))
+ ImportRecord.Tag.bun
+ else
+ ImportRecord.Tag.hardcoded;
+ import_record.is_bundled = false;
+ continue;
+ }
}
if (bundler.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |*_resolved_import| {
@@ -1740,6 +1753,17 @@ pub fn processFile(this: *GenerateNodeModuleBundle, worker: *ThreadPool.Worker,
if (bundler.options.platform.isBun()) {
if (JSC.DisabledModule.has(import_record.path.text)) {
import_record.path.is_disabled = true;
+ import_record.is_bundled = true;
+ continue;
+ }
+
+ if (JSC.HardcodedModule.LinkerMap.get(import_record.path.text)) |remapped| {
+ import_record.path.text = remapped;
+ import_record.tag = if (strings.eqlComptime(remapped, "bun"))
+ ImportRecord.Tag.bun
+ else
+ ImportRecord.Tag.hardcoded;
+ import_record.is_bundled = false;
continue;
}
}
diff --git a/src/import_record.zig b/src/import_record.zig
index e287db36e..a16e5a8ad 100644
--- a/src/import_record.zig
+++ b/src/import_record.zig
@@ -151,6 +151,7 @@ pub const ImportRecord = struct {
jsx_classic,
bun,
bun_test,
+ hardcoded,
};
pub const PrintMode = enum {
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 859cb4a9b..06b8d255e 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -39,6 +39,7 @@ pub const ExprNodeList = js_ast.ExprNodeList;
pub const StmtNodeList = js_ast.StmtNodeList;
pub const BindingNodeList = js_ast.BindingNodeList;
const ComptimeStringMap = @import("./comptime_string_map.zig").ComptimeStringMap;
+const JSC = @import("javascript_core");
fn _disabledAssert(_: bool) void {
if (!Environment.allow_assert) @compileLog("assert is missing an if (Environment.allow_assert)");
@@ -2424,54 +2425,8 @@ pub const Parser = struct {
}
const uses_dirname = p.symbols.items[p.dirname_ref.innerIndex()].use_count_estimate > 0;
- const uses_dynamic_require = p.options.features.dynamic_require and p.symbols.items[p.require_ref.innerIndex()].use_count_estimate > 0;
const uses_filename = p.symbols.items[p.filename_ref.innerIndex()].use_count_estimate > 0;
- if (uses_dynamic_require) {
- var declared_symbols = try p.allocator.alloc(js_ast.DeclaredSymbol, 1);
- var decls = p.allocator.alloc(G.Decl, 1) catch unreachable;
- var part_stmts = p.allocator.alloc(Stmt, 1) catch unreachable;
- var exprs = p.allocator.alloc(Expr, 1) catch unreachable;
- exprs[0] = p.e(E.ImportMeta{}, logger.Loc.Empty);
- // var require = import.meta.require.bind(import.meta)
- decls[0] = .{
- .binding = p.b(B.Identifier{ .ref = p.require_ref }, logger.Loc.Empty),
- .value = p.e(
- E.Call{
- .target = p.e(
- E.Dot{
- .target = p.e(
- E.Dot{
- .target = p.e(E.ImportMeta{}, logger.Loc.Empty),
- .name = "require",
- .name_loc = logger.Loc.Empty,
- },
- logger.Loc.Empty,
- ),
- .name = "bind",
- .name_loc = logger.Loc.Empty,
- },
- logger.Loc.Empty,
- ),
- .args = ExprNodeList.init(exprs),
- },
- logger.Loc.Empty,
- ),
- };
-
- declared_symbols[0] = .{ .ref = p.require_ref, .is_top_level = true };
-
- part_stmts[0] = p.s(S.Local{
- .kind = .k_var,
- .decls = decls,
- }, logger.Loc.Empty);
- before.append(js_ast.Part{
- .stmts = part_stmts,
- .declared_symbols = declared_symbols,
- .tag = .dirname_filename,
- }) catch unreachable;
- }
-
if (uses_dirname or uses_filename) {
const count = @as(usize, @boolToInt(uses_dirname)) + @as(usize, @boolToInt(uses_filename));
var declared_symbols = try p.allocator.alloc(js_ast.DeclaredSymbol, count);
@@ -2524,7 +2479,7 @@ pub const Parser = struct {
exports_kind = .esm;
} else if (uses_exports_ref or uses_module_ref or p.has_top_level_return) {
exports_kind = .cjs;
- if (p.options.transform_require_to_import) {
+ if (p.options.transform_require_to_import or (p.options.features.dynamic_require and !p.options.enable_bundling)) {
var args = p.allocator.alloc(Expr, 2) catch unreachable;
if (p.runtime_imports.__exportDefault == null and p.has_export_default) {
@@ -2696,9 +2651,18 @@ pub const Parser = struct {
declared_symbols_i += 1;
const automatic_identifier = p.e(E.ImportIdentifier{ .ref = automatic_namespace_ref }, loc);
+
+ // We do not mark this as .require becuase we are already wrapping it manually.
+ // unless it's bun and you're not bundling
+ const use_automatic_identifier = (p.options.can_import_from_bundle or p.options.enable_bundling or !p.options.features.allow_runtime);
+ const import_record_kind = if (use_automatic_identifier or !p.options.features.dynamic_require) ImportKind.internal else ImportKind.require;
+ const import_record_id = p.addImportRecord(import_record_kind, loc, p.options.jsx.import_source);
+
const dot_call_target = brk: {
- if (p.options.can_import_from_bundle or p.options.enable_bundling or !p.options.features.allow_runtime) {
+ if (use_automatic_identifier) {
break :brk automatic_identifier;
+ } else if (p.options.features.dynamic_require) {
+ break :brk p.e(E.Require{ .import_record_index = import_record_id }, loc);
} else {
require_call_args_base[require_call_args_i] = automatic_identifier;
require_call_args_i += 1;
@@ -2770,21 +2734,21 @@ pub const Parser = struct {
decl_i += 1;
}
- // We do not mark this as .require becuase we are already wrapping it manually.
- const import_record_id = p.addImportRecord(.internal, loc, p.options.jsx.import_source);
p.import_records.items[import_record_id].tag = .jsx_import;
- // When everything is CommonJS
- // We import JSX like this:
- // var {jsxDev} = require("react/jsx-dev")
+ if (dot_call_target.data != .e_require) {
+ // When everything is CommonJS
+ // We import JSX like this:
+ // var {jsxDev} = require("react/jsx-dev")
+ jsx_part_stmts[stmt_i] = p.s(S.Import{
+ .namespace_ref = automatic_namespace_ref,
+ .star_name_loc = loc,
+ .is_single_line = true,
+ .import_record_index = import_record_id,
+ }, loc);
- jsx_part_stmts[stmt_i] = p.s(S.Import{
- .namespace_ref = automatic_namespace_ref,
- .star_name_loc = loc,
- .is_single_line = true,
- .import_record_index = import_record_id,
- }, loc);
+ stmt_i += 1;
+ }
- stmt_i += 1;
p.named_imports.put(
automatic_namespace_ref,
js_ast.NamedImport{
@@ -2802,12 +2766,14 @@ pub const Parser = struct {
if (jsx_classic_symbol.use_count_estimate > 0) {
const classic_identifier = p.e(E.ImportIdentifier{ .ref = classic_namespace_ref }, loc);
-
+ const import_record_id = p.addImportRecord(.require, loc, p.options.jsx.classic_import_source);
const dot_call_target = brk: {
// var react = $aopaSD123();
if (p.options.can_import_from_bundle or p.options.enable_bundling or !p.options.features.allow_runtime) {
break :brk classic_identifier;
+ } else if (p.options.features.dynamic_require) {
+ break :brk p.e(E.Require{ .import_record_index = import_record_id }, loc);
} else {
const require_call_args_start = require_call_args_i;
require_call_args_base[require_call_args_i] = classic_identifier;
@@ -2861,15 +2827,19 @@ pub const Parser = struct {
};
decl_i += 1;
}
- const import_record_id = p.addImportRecord(.require, loc, p.options.jsx.classic_import_source);
- jsx_part_stmts[stmt_i] = p.s(S.Import{
- .namespace_ref = classic_namespace_ref,
- .star_name_loc = loc,
- .is_single_line = true,
- .import_record_index = import_record_id,
- }, loc);
+
+ if (dot_call_target.data != .e_require) {
+ jsx_part_stmts[stmt_i] = p.s(S.Import{
+ .namespace_ref = classic_namespace_ref,
+ .star_name_loc = loc,
+ .is_single_line = true,
+ .import_record_index = import_record_id,
+ }, loc);
+ stmt_i += 1;
+ }
+
p.import_records.items[import_record_id].tag = .jsx_classic;
- stmt_i += 1;
+
p.named_imports.put(
classic_namespace_ref,
js_ast.NamedImport{
@@ -4230,36 +4200,6 @@ fn NewParser_(
const pathname = str.string(p.allocator) catch unreachable;
- // When we know that we support dynamically requiring this file type
- // we can avoid eager loading it
- // instead, we can just use the require() function directly.
- if (p.options.features.dynamic_require and
- !p.options.enable_bundling and
- (strings.endsWithComptime(pathname, ".json") or
- // strings.endsWithComptime(pathname, ".toml") or
- strings.endsWithComptime(pathname, ".node")))
- {
- p.ignoreUsage(p.require_ref);
- var args = p.allocator.alloc(Expr, 1) catch unreachable;
- args[0] = arg;
-
- return p.e(
- E.Call{
- .target = p.e(
- E.Dot{
- .target = p.e(E.ImportMeta{}, arg.loc),
- .name = "require",
- .name_loc = arg.loc,
- },
- arg.loc,
- ),
- .args = js_ast.ExprNodeList.init(args),
- .close_paren_loc = arg.loc,
- },
- arg.loc,
- );
- }
-
const import_record_index = p.addImportRecord(.require, arg.loc, pathname);
p.import_records.items[import_record_index].handles_import_errors = p.fn_or_arrow_data_visit.try_body_count != 0;
p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable;
@@ -14897,6 +14837,24 @@ fn NewParser_(
}
}
+ if (p.options.features.dynamic_require) {
+ p.ignoreUsage(p.require_ref);
+ return p.e(
+ E.Call{
+ .target = p.e(E.Dot{
+ .target = p.e(E.ImportMeta{}, expr.loc),
+ .name = "require",
+ .name_loc = expr.loc,
+ }, expr.loc),
+ .args = e_.args,
+ .close_paren_loc = e_.close_paren_loc,
+ .optional_chain = e_.optional_chain,
+ .can_be_unwrapped_if_unused = e_.can_be_unwrapped_if_unused,
+ },
+ expr.loc,
+ );
+ }
+
if (p.options.warn_about_unbundled_modules) {
const r = js_lexer.rangeOfIdentifier(p.source, e_.target.loc);
p.log.addRangeDebug(p.source, r, "This call to \"require\" will not be bundled because it has multiple arguments") catch unreachable;
@@ -18835,7 +18793,7 @@ fn NewParser_(
this.require_transposer = @TypeOf(this.require_transposer).init(this);
this.require_resolve_transposer = @TypeOf(this.require_resolve_transposer).init(this);
- if (opts.features.top_level_await) {
+ if (opts.features.top_level_await or comptime only_scan_imports_and_do_not_visit) {
this.fn_or_arrow_data_parse.allow_await = .allow_expr;
this.fn_or_arrow_data_parse.is_top_level = true;
}
diff --git a/src/js_printer.zig b/src/js_printer.zig
index ec2f1c0be..8f5f47e20 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -633,28 +633,42 @@ pub fn NewPrinter(
}
fn printBunJestImportStatement(p: *Printer, import: S.Import) void {
- printInternalBunImport(p, import, "globalThis.Bun.jest(import.meta.path)");
+ if (comptime !is_bun_platform) unreachable;
+
+ printInternalBunImport(p, import, @TypeOf("globalThis.Bun.jest(import.meta.path)"), "globalThis.Bun.jest(import.meta.path)");
}
fn printGlobalBunImportStatement(p: *Printer, import: S.Import) void {
- printInternalBunImport(p, import, "globalThis.Bun");
+ if (comptime !is_bun_platform) unreachable;
+ printInternalBunImport(p, import, @TypeOf("globalThis.Bun"), "globalThis.Bun");
+ }
+
+ fn printHardcodedImportStatement(p: *Printer, import: S.Import) void {
+ if (comptime !is_bun_platform) unreachable;
+ printInternalBunImport(p, import, void, void{});
}
- fn printInternalBunImport(p: *Printer, import: S.Import, comptime statement: anytype) void {
- p.print("var ");
+ fn printInternalBunImport(p: *Printer, import: S.Import, comptime Statement: type, statement: Statement) void {
+ if (comptime !is_bun_platform) unreachable;
if (import.star_name_loc != null) {
+ p.print("var ");
p.printSymbol(import.namespace_ref);
p.printSpace();
p.print("=");
p.printSpaceBeforeIdentifier();
- p.print(statement);
+ if (comptime Statement == void) {
+ p.printRequireOrImportExpr(import.import_record_index, &.{}, Level.lowest, ExprFlag.None());
+ } else {
+ p.print(statement);
+ }
+
p.printSemicolonAfterStatement();
p.printIndent();
- p.print("var ");
}
if (import.items.len > 0) {
+ p.print("var ");
p.print("{ ");
if (!import.is_single_line) {
p.print("\n");
@@ -691,7 +705,13 @@ pub fn NewPrinter(
}
if (import.star_name_loc == null) {
- p.print("} = " ++ statement);
+ if (comptime Statement == void) {
+ p.print("} = ");
+ p.printRequireOrImportExpr(import.import_record_index, &.{}, Level.lowest, ExprFlag.None());
+ } else {
+ p.print("} = ");
+ p.print(statement);
+ }
} else {
p.print("} =");
p.printSpaceBeforeIdentifier();
@@ -699,6 +719,17 @@ pub fn NewPrinter(
}
p.printSemicolonAfterStatement();
+ } else if (import.default_name) |default| {
+ p.print("var ");
+ p.printSymbol(default.ref.?);
+ if (comptime Statement == void) {
+ p.print(" = ");
+ p.printRequireOrImportExpr(import.import_record_index, &.{}, Level.lowest, ExprFlag.None());
+ } else {
+ p.print(" = ");
+ p.print(statement);
+ }
+ p.printSemicolonAfterStatement();
}
}
@@ -1362,7 +1393,12 @@ pub fn NewPrinter(
return;
}
- p.printSymbol(p.options.require_ref.?);
+ if (comptime is_bun_platform) {
+ p.print("import.meta.require");
+ } else {
+ p.printSymbol(p.options.require_ref.?);
+ }
+
p.print("(");
p.printQuotedUTF8(record.path.text, true);
p.print(")");
@@ -3604,18 +3640,20 @@ pub fn NewPrinter(
p.printSpaceBeforeIdentifier();
if (comptime is_bun_platform) {
- if (record.tag != .none) {
- switch (record.tag) {
- .bun_test => {
- p.printBunJestImportStatement(s.*);
- return;
- },
- .bun => {
- p.printGlobalBunImportStatement(s.*);
- return;
- },
- else => {},
- }
+ switch (record.tag) {
+ .bun_test => {
+ p.printBunJestImportStatement(s.*);
+ return;
+ },
+ .bun => {
+ p.printGlobalBunImportStatement(s.*);
+ return;
+ },
+ .hardcoded => {
+ p.printHardcodedImportStatement(s.*);
+ return;
+ },
+ else => {},
}
}
diff --git a/src/linker.zig b/src/linker.zig
index 8be0b14b2..449367b00 100644
--- a/src/linker.zig
+++ b/src/linker.zig
@@ -210,6 +210,7 @@ pub const Linker = struct {
comptime allow_import_from_bundle: bool,
comptime is_bun: bool,
) !void {
+ const supports_dynamic_require = comptime is_bun;
const source_dir = file_path.sourceDir();
var externals = std.ArrayList(u32).init(linker.allocator);
var needs_bundle = false;
@@ -264,6 +265,7 @@ pub const Linker = struct {
if (comptime is_bun) {
if (JSC.HardcodedModule.LinkerMap.get(import_record.path.text)) |replacement| {
import_record.path.text = replacement;
+ import_record.tag = .hardcoded;
externals.append(record_index) catch unreachable;
continue;
}
@@ -274,11 +276,6 @@ pub const Linker = struct {
continue;
}
- if (strings.eqlComptime(import_record.path.text, "bun")) {
- import_record.tag = .bun;
- continue;
- }
-
// if (strings.eqlComptime(import_record.path.text, "process")) {
// import_record.path.text = "node:process";
// externals.append(record_index) catch unreachable;
@@ -486,23 +483,25 @@ pub const Linker = struct {
import_path_format,
) catch continue;
- // If we're importing a CommonJS module as ESM
- // We need to do the following transform:
- // import React from 'react';
- // =>
- // import {_require} from 'RUNTIME_IMPORTS';
- // import * as react_module from 'react';
- // var React = _require(react_module).default;
- // UNLESS it's a namespace import
- // If it's a namespace import, assume it's safe.
- // We can do this in the printer instead of creating a bunch of AST nodes here.
- // But we need to at least tell the printer that this needs to happen.
- if (loader != .napi and resolved_import.shouldAssumeCommonJS(import_record.kind)) {
- import_record.wrap_with_to_module = true;
- import_record.module_id = @truncate(u32, std.hash.Wyhash.hash(0, path.pretty));
+ if (comptime !supports_dynamic_require) {
+ // If we're importing a CommonJS module as ESM
+ // We need to do the following transform:
+ // import React from 'react';
+ // =>
+ // import {_require} from 'RUNTIME_IMPORTS';
+ // import * as react_module from 'react';
+ // var React = _require(react_module).default;
+ // UNLESS it's a namespace import
+ // If it's a namespace import, assume it's safe.
+ // We can do this in the printer instead of creating a bunch of AST nodes here.
+ // But we need to at least tell the printer that this needs to happen.
+ if (loader != .napi and resolved_import.shouldAssumeCommonJS(import_record.kind)) {
+ import_record.wrap_with_to_module = true;
+ import_record.module_id = @truncate(u32, std.hash.Wyhash.hash(0, path.pretty));
- result.ast.needs_runtime = true;
- needs_require = true;
+ result.ast.needs_runtime = true;
+ needs_require = true;
+ }
}
} else |err| {
switch (err) {
@@ -570,6 +569,7 @@ pub const Linker = struct {
}
}
},
+
else => {},
}
if (had_resolve_errors) return error.ResolveError;
@@ -594,34 +594,36 @@ pub const Linker = struct {
import_records = new_import_records;
}
- // We _assume_ you're importing ESM.
- // But, that assumption can be wrong without parsing code of the imports.
- // That's where in here, we inject
- // > import {require} from 'bun:wrap';
- // Since they definitely aren't using require, we don't have to worry about the symbol being renamed.
- if (needs_require and !result.ast.uses_require_ref) {
- result.ast.uses_require_ref = true;
- require_part_import_clauses[0] = js_ast.ClauseItem{
- .alias = require_alias,
- .original_name = "",
- .alias_loc = logger.Loc.Empty,
- .name = js_ast.LocRef{
+ if (comptime !supports_dynamic_require) {
+ // We _assume_ you're importing ESM.
+ // But, that assumption can be wrong without parsing code of the imports.
+ // That's where in here, we inject
+ // > import {require} from 'bun:wrap';
+ // Since they definitely aren't using require, we don't have to worry about the symbol being renamed.
+ if (needs_require and !result.ast.uses_require_ref) {
+ result.ast.uses_require_ref = true;
+ require_part_import_clauses[0] = js_ast.ClauseItem{
+ .alias = require_alias,
+ .original_name = "",
+ .alias_loc = logger.Loc.Empty,
+ .name = js_ast.LocRef{
+ .loc = logger.Loc.Empty,
+ .ref = result.ast.require_ref,
+ },
+ };
+
+ require_part_import_statement = js_ast.S.Import{
+ .namespace_ref = Ref.None,
+ .items = std.mem.span(&require_part_import_clauses),
+ .import_record_index = result.ast.runtime_import_record_id.?,
+ };
+ require_part_stmts[0] = js_ast.Stmt{
+ .data = .{ .s_import = &require_part_import_statement },
.loc = logger.Loc.Empty,
- .ref = result.ast.require_ref,
- },
- };
-
- require_part_import_statement = js_ast.S.Import{
- .namespace_ref = Ref.None,
- .items = std.mem.span(&require_part_import_clauses),
- .import_record_index = result.ast.runtime_import_record_id.?,
- };
- require_part_stmts[0] = js_ast.Stmt{
- .data = .{ .s_import = &require_part_import_statement },
- .loc = logger.Loc.Empty,
- };
+ };
- result.ast.prepend_part = js_ast.Part{ .stmts = std.mem.span(&require_part_stmts) };
+ result.ast.prepend_part = js_ast.Part{ .stmts = std.mem.span(&require_part_stmts) };
+ }
}
}
@@ -778,6 +780,7 @@ pub const Linker = struct {
.wasm, .file => {
import_record.print_mode = .import_path;
},
+
else => {},
}
}
diff --git a/src/options.zig b/src/options.zig
index 202e99434..044a4451b 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -1216,6 +1216,8 @@ pub const BundleOptions = struct {
tree_shaking: bool = false,
sourcemap: SourceMapOption = SourceMapOption.none,
+ disable_transpilation: bool = false,
+
pub inline fn cssImportBehavior(this: *const BundleOptions) Api.CssInJsBehavior {
switch (this.platform) {
.neutral, .browser => {
diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig
index cd6d79738..10cea0c37 100644
--- a/src/resolver/resolve_path.zig
+++ b/src/resolver/resolve_path.zig
@@ -306,7 +306,7 @@ pub fn dirname(str: []const u8, comptime platform: Platform) []const u8 {
threadlocal var relative_from_buf: [4096]u8 = undefined;
threadlocal var relative_to_buf: [4096]u8 = undefined;
pub fn relative(from: []const u8, to: []const u8) []const u8 {
- if (FeatureFlags.use_std_path_relative) {
+ if (comptime FeatureFlags.use_std_path_relative) {
var relative_allocator = std.heap.FixedBufferAllocator.init(&relative_from_buf);
return relativeAlloc(&relative_allocator.allocator, from, to) catch unreachable;
} else {
diff --git a/src/runtime.js b/src/runtime.js
index efb0265e4..c70f1207d 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -52,8 +52,8 @@ export var __toModule = (module) => {
);
};
-var tagSymbol = Symbol("CommonJSTransformed");
-var cjsRequireSymbol = Symbol("CommonJS");
+var tagSymbol = Symbol.for("CommonJSTransformed");
+var cjsRequireSymbol = Symbol.for("CommonJS");
export var __commonJS = (cb, name) => {
var mod;
var has_run = false;
diff --git a/src/runtime.zig b/src/runtime.zig
index 33561f9ce..2cce4a87f 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -286,16 +286,13 @@ pub const Runtime = struct {
/// See also https://github.com/babel/babel/pull/2972
/// See also https://github.com/facebook/react/issues/5138
jsx_optimization_inline: bool = false,
-
jsx_optimization_hoist: bool = false,
trim_unused_imports: bool = false,
should_fold_numeric_constants: bool = false,
- /// inject this at the top of the file?
- /// ```js
- /// var require = import.meta.require.bind(import.meta);
- /// ```
+ /// Use `import.meta.require()` instead of require()?
+ /// This is only supported in Bun.
dynamic_require: bool = false,
replace_exports: ReplaceableExport.Map = .{},
diff --git a/test/bun.js/import-meta.test.js b/test/bun.js/import-meta.test.js
index 0e2faa903..f2cd996cd 100644
--- a/test/bun.js/import-meta.test.js
+++ b/test/bun.js/import-meta.test.js
@@ -18,12 +18,52 @@ it("import.meta.resolveSync", () => {
).toBe(import.meta.path);
});
-it("import.meta.require", () => {
+it("import.meta.require (json)", () => {
expect(import.meta.require("./require-json.json").hello).toBe(sync.hello);
const require = Module.createRequire(import.meta.path);
expect(require("./require-json.json").hello).toBe(sync.hello);
});
+it("import.meta.require (javascript)", () => {
+ expect(import.meta.require("./require-js.js").hello).toBe(sync.hello);
+ const require = Module.createRequire(import.meta.path);
+ expect(require("./require-js.js").hello).toBe(sync.hello);
+});
+
+it("import.meta.require (javascript, live bindings)", () => {
+ var Source = import.meta.require("./import.live.decl.js");
+
+ // require transpiles to import.meta.require
+ var ReExport = require("./import.live.rexport.js");
+
+ // dynamic require (string interpolation that way forces it to be dynamic)
+ var ReExportDynamic = require(`./import.live.${"rexport"
+ .split("")
+ .join("")}.js`);
+
+ expect(Source.foo).toBe(1);
+ Source.setFoo(Source.foo + 1);
+
+ expect(ReExport.foo).toBe(2);
+ expect(Source.foo).toBe(2);
+ expect(ReExportDynamic.foo).toBe(2);
+
+ Source.setFoo(Source.foo + 1);
+
+ var { Namespace } = require("./import.live.rexport-require.js");
+
+ expect(Namespace).toBe(Source);
+ expect(ReExport.foo).toBe(3);
+ expect(Source.foo).toBe(3);
+ expect(Namespace.foo).toBe(3);
+
+ ReExport.setFoo(ReExport.foo + 1);
+
+ expect(ReExport.foo).toBe(4);
+ expect(Source.foo).toBe(4);
+ expect(Namespace.foo).toBe(4);
+});
+
it("import.meta.dir", () => {
expect(dir.endsWith("/bun/test/bun.js")).toBe(true);
});
diff --git a/test/bun.js/import.live.decl.js b/test/bun.js/import.live.decl.js
new file mode 100644
index 000000000..46e67c9bd
--- /dev/null
+++ b/test/bun.js/import.live.decl.js
@@ -0,0 +1,4 @@
+export var foo = 1;
+export function setFoo(val) {
+ foo = val;
+}
diff --git a/test/bun.js/import.live.rexport-require.js b/test/bun.js/import.live.rexport-require.js
new file mode 100644
index 000000000..10c993e08
--- /dev/null
+++ b/test/bun.js/import.live.rexport-require.js
@@ -0,0 +1 @@
+export const Namespace = import.meta.require("./import.live.decl.js");
diff --git a/test/bun.js/import.live.rexport.js b/test/bun.js/import.live.rexport.js
new file mode 100644
index 000000000..e4accd2ba
--- /dev/null
+++ b/test/bun.js/import.live.rexport.js
@@ -0,0 +1,2 @@
+export { foo, setFoo } from "./import.live.decl";
+import { foo as bar } from "./import.live.decl";
diff --git a/test/bun.js/require-js-top-level-await.js b/test/bun.js/require-js-top-level-await.js
new file mode 100644
index 000000000..16bff41c3
--- /dev/null
+++ b/test/bun.js/require-js-top-level-await.js
@@ -0,0 +1 @@
+export const fail = await 1;
diff --git a/test/bun.js/require-js.js b/test/bun.js/require-js.js
new file mode 100644
index 000000000..36fc31432
--- /dev/null
+++ b/test/bun.js/require-js.js
@@ -0,0 +1,2 @@
+import { hello } from "./require-js2.js";
+export { hello };
diff --git a/test/bun.js/require-js2.js b/test/bun.js/require-js2.js
new file mode 100644
index 000000000..518e69641
--- /dev/null
+++ b/test/bun.js/require-js2.js
@@ -0,0 +1 @@
+export const hello = -123;