diff options
30 files changed, 914 insertions, 249 deletions
@@ -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; |