diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 4bda9e77fa..115fc7a49b 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -627,6 +627,19 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, {}); } + target->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), value, 0); + target->hasEvaluated = true; + RELEASE_AND_RETURN(scope, target); + + } + // TOML and JSONC may go through here + else if (res->result.value.tag == SyntheticModuleType::ExportsObject) { + JSC::JSValue value = JSC::JSValue::decode(res->result.value.jsvalue_for_export); + if (!value) { + JSC::throwException(globalObject, scope, JSC::createSyntaxError(globalObject, "Failed to parse Object"_s)); + RELEASE_AND_RETURN(scope, {}); + } + target->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), value, 0); target->hasEvaluated = true; RELEASE_AND_RETURN(scope, target); @@ -807,6 +820,23 @@ static JSValue fetchESMSourceCode( JSC::ensureStillAliveHere(value); return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source))); } + // TOML and JSONC may go through here + else if (res->result.value.tag == SyntheticModuleType::ExportsObject) { + JSC::JSValue value = JSC::JSValue::decode(res->result.value.jsvalue_for_export); + if (!value) { + return reject(JSC::JSValue(JSC::createSyntaxError(globalObject, "Failed to parse Object"_s))); + } + + // JSON can become strings, null, numbers, booleans so we must handle "export default 123" + auto function = generateJSValueModuleSourceCode( + globalObject, + value); + auto source = JSC::SourceCode( + JSC::SyntheticSourceProvider::create(WTFMove(function), + JSC::SourceOrigin(), specifier->toWTFString(BunString::ZeroCopy))); + JSC::ensureStillAliveHere(value); + return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source))); + } return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(Zig::SourceProvider::create(globalObject, res->result.value)))); diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 76476df03d..11a1a42e97 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -212,6 +212,8 @@ pub const ResolvedSource = extern struct { allocator: ?*anyopaque = null, + jsvalue_for_export: JSC.JSValue = .zero, + tag: Tag = Tag.javascript, /// This is for source_code diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index e752349035..c68a1d3dab 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -96,6 +96,7 @@ typedef struct ResolvedSource { uint32_t commonJSExportsLen; uint32_t hash; void* allocator; + JSC::EncodedJSValue jsvalue_for_export; uint32_t tag; bool needsDeref; } ResolvedSource; diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 1b5847509f..1e08e4b6e4 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -1655,6 +1655,7 @@ pub const ModuleLoader = struct { .dont_bundle_twice = true, .allow_commonjs = true, .inject_jest_globals = jsc_vm.bundler.options.rewrite_jest_for_tests and is_main, + .keep_json_and_toml_as_one_statement = true, .set_breakpoint_on_first_line = is_main and jsc_vm.debugger != null and jsc_vm.debugger.?.set_breakpoint_on_first_line and @@ -1778,6 +1779,17 @@ pub const ModuleLoader = struct { }; } + if (loader == .json or loader == .toml) { + return ResolvedSource{ + .allocator = null, + .specifier = input_specifier, + .source_url = input_specifier.createIfDifferent(path.text), + .hash = 0, + .jsvalue_for_export = parse_result.ast.parts.@"[0]"().stmts[0].data.s_expr.value.toJS(allocator, globalObject orelse jsc_vm.global) catch @panic("Unexpected JS error"), + .tag = .exports_object, + }; + } + if (parse_result.already_bundled) { return ResolvedSource{ .allocator = null, diff --git a/src/bundler.zig b/src/bundler.zig index b82421b983..d15e7913a1 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -1272,6 +1272,8 @@ pub const Bundler = struct { allow_commonjs: bool = false, runtime_transpiler_cache: ?*bun.JSC.RuntimeTranspilerCache = null, + + keep_json_and_toml_as_one_statement: bool = false, }; pub fn parse( @@ -1485,6 +1487,14 @@ pub const Bundler = struct { var symbols: []js_ast.Symbol = &.{}; const parts = brk: { + if (this_parse.keep_json_and_toml_as_one_statement) { + var stmts = allocator.alloc(js_ast.Stmt, 1) catch unreachable; + stmts[0] = js_ast.Stmt.allocate(allocator, js_ast.S.SExpr, js_ast.S.SExpr{ .value = expr }, logger.Loc{ .start = 0 }); + var parts_ = allocator.alloc(js_ast.Part, 1) catch unreachable; + parts_[0] = js_ast.Part{ .stmts = stmts }; + break :brk parts_; + } + if (expr.data == .e_object) { const properties: []js_ast.G.Property = expr.data.e_object.properties.slice(); if (properties.len > 0) { diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index cb121c8f01..825203ebbd 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -373,6 +373,7 @@ pub const ResolvedSourceTag = enum(u32) { file = 4, esm = 5, json_for_object_loader = 6, + exports_object = 7, // Built in modules are loaded through InternalModuleRegistry by numerical ID. // In this enum are represented as \`(1 << 9) & id\` @@ -396,6 +397,7 @@ writeIfNotChanged( File = 4, ESM = 5, JSONForObjectLoader = 6, + ExportsObject = 7, // Built in modules are loaded through InternalModuleRegistry by numerical ID. // In this enum are represented as \`(1 << 9) & id\`