diff --git a/src/runtime/bake/bake_body.rs b/src/runtime/bake/bake_body.rs index 6f78303e044..80eb659aca1 100644 --- a/src/runtime/bake/bake_body.rs +++ b/src/runtime/bake/bake_body.rs @@ -210,14 +210,21 @@ impl UserOptions { } if let Some(js_options) = get_optional_value(config, global, b"bundlerOptions")? { + if !js_options.is_object() { + return Err(global.throw_invalid_arguments(format_args!( + "Expected 'bundlerOptions' to be an object" + ))); + } if let Some(server_options) = get_optional_value(js_options, global, b"server")? { - bundler_options.server = BuildConfigSubset::from_js(global, server_options)?; + bundler_options.server = + BuildConfigSubset::from_js(global, server_options, "server")?; } if let Some(client_options) = get_optional_value(js_options, global, b"client")? { - bundler_options.client = BuildConfigSubset::from_js(global, client_options)?; + bundler_options.client = + BuildConfigSubset::from_js(global, client_options, "client")?; } if let Some(ssr_options) = get_optional_value(js_options, global, b"ssr")? { - bundler_options.ssr = BuildConfigSubset::from_js(global, ssr_options)?; + bundler_options.ssr = BuildConfigSubset::from_js(global, ssr_options, "ssr")?; } } @@ -408,9 +415,20 @@ pub struct BuildConfigSubset { } impl BuildConfigSubset { - pub fn from_js(global: &JSGlobalObject, js_options: JSValue) -> JsResult { + pub fn from_js( + global: &JSGlobalObject, + js_options: JSValue, + property_name: &str, + ) -> JsResult { let mut options = BuildConfigSubset::default(); + if !js_options.is_object() { + return Err(global.throw_invalid_arguments(format_args!( + "Expected 'bundlerOptions.{}' to be an object", + property_name + ))); + } + 'brk: { let Some(val) = get_optional_value(js_options, global, b"sourcemap")? else { break 'brk; @@ -428,25 +446,26 @@ impl BuildConfigSubset { )); } - 'brk: { - let Some(minify_options) = get_optional_value(js_options, global, b"minify")? else { - break 'brk; - }; - if minify_options.is_boolean() && minify_options.as_boolean() { - options.minify_syntax = Some(minify_options.as_boolean()); - options.minify_identifiers = Some(minify_options.as_boolean()); - options.minify_whitespace = Some(minify_options.as_boolean()); - break 'brk; - } - - if let Some(value) = get_boolean_loose(minify_options, global, b"whitespace")? { - options.minify_whitespace = Some(value); - } - if let Some(value) = get_boolean_loose(minify_options, global, b"syntax")? { + if let Some(minify_options) = get_optional_value(js_options, global, b"minify")? { + if minify_options.is_boolean() { + let value = minify_options.as_boolean(); options.minify_syntax = Some(value); - } - if let Some(value) = get_boolean_loose(minify_options, global, b"identifiers")? { options.minify_identifiers = Some(value); + options.minify_whitespace = Some(value); + } else if minify_options.is_object() { + if let Some(value) = get_boolean_loose(minify_options, global, b"whitespace")? { + options.minify_whitespace = Some(value); + } + if let Some(value) = get_boolean_loose(minify_options, global, b"syntax")? { + options.minify_syntax = Some(value); + } + if let Some(value) = get_boolean_loose(minify_options, global, b"identifiers")? { + options.minify_identifiers = Some(value); + } + } else { + return Err(global.throw_invalid_arguments(format_args!( + "Expected minify to be a boolean or an object" + ))); } } diff --git a/test/js/bun/http/bun-serve-args.test.ts b/test/js/bun/http/bun-serve-args.test.ts index 54a44c192d3..347c638fc0e 100644 --- a/test/js/bun/http/bun-serve-args.test.ts +++ b/test/js/bun/http/bun-serve-args.test.ts @@ -670,3 +670,47 @@ describe("Bun.serve unix socket validation", () => { } }); }); + +describe("app.bundlerOptions validation", () => { + test.each([ + ["bundlerOptions is not an object", { bundlerOptions: 123 }, "Expected 'bundlerOptions' to be an object"], + [ + "bundlerOptions.server is not an object", + { bundlerOptions: { server: 123 } }, + "Expected 'bundlerOptions.server' to be an object", + ], + [ + "bundlerOptions.client is not an object", + { bundlerOptions: { client: "abc" } }, + "Expected 'bundlerOptions.client' to be an object", + ], + [ + "bundlerOptions.ssr is not an object", + { bundlerOptions: { ssr: true } }, + "Expected 'bundlerOptions.ssr' to be an object", + ], + [ + "minify is not a boolean or object", + { bundlerOptions: { ssr: { minify: 10 } } }, + "Expected minify to be a boolean or an object", + ], + ])("throws when %s", (_, app, message) => { + expect(() => { + // @ts-expect-error - Testing invalid runtime input + serve({ port: 0, app }); + }).toThrow( + expect.objectContaining({ + name: "TypeError", + code: "ERR_INVALID_ARG_TYPE", + message, + }), + ); + }); + + test("minify: false does not crash", () => { + expect(() => { + // @ts-expect-error - Testing runtime input + serve({ port: 0, app: { bundlerOptions: { ssr: { minify: false } } } }); + }).toThrow("'app' is missing 'framework'"); + }); +});