diff --git a/src/bake/bake.zig b/src/bake/bake.zig index cd621eb3de4..0caab14b920 100644 --- a/src/bake/bake.zig +++ b/src/bake/bake.zig @@ -68,6 +68,9 @@ pub const UserOptions = struct { } if (try config.getOptional(global, "bundlerOptions", JSValue)) |js_options| { + if (!js_options.isObject()) { + return global.throwInvalidArguments("'" ++ api_name ++ ".bundlerOptions' must be an object", .{}); + } if (try js_options.getOptional(global, "server", JSValue)) |server_options| { bundler_options.server = try BuildConfigSubset.fromJS(global, server_options); } @@ -205,6 +208,10 @@ const BuildConfigSubset = struct { pub fn fromJS(global: *jsc.JSGlobalObject, js_options: JSValue) bun.JSError!BuildConfigSubset { var options = BuildConfigSubset{}; + if (!js_options.isObject()) { + return global.throwInvalidArguments("Expected bundler options to be an object", .{}); + } + if (try js_options.getOptional(global, "sourcemap", JSValue)) |val| brk: { if (try bun.schema.api.SourceMapMode.fromJS(global, val)) |sourcemap| { options.source_map = sourcemap; @@ -214,22 +221,24 @@ const BuildConfigSubset = struct { return bun.jsc.Node.validators.throwErrInvalidArgType(global, "sourcemap", .{}, "\"inline\" | \"external\" | \"linked\"", val); } - if (try js_options.getOptional(global, "minify", JSValue)) |minify_options| brk: { - if (minify_options.isBoolean() and minify_options.asBoolean()) { - options.minify_syntax = minify_options.asBoolean(); - options.minify_identifiers = minify_options.asBoolean(); - options.minify_whitespace = minify_options.asBoolean(); - break :brk; - } - - if (try minify_options.getBooleanLoose(global, "whitespace")) |value| { - options.minify_whitespace = value; - } - if (try minify_options.getBooleanLoose(global, "syntax")) |value| { + if (try js_options.getOptional(global, "minify", JSValue)) |minify_options| { + if (minify_options.isBoolean()) { + const value = minify_options.asBoolean(); options.minify_syntax = value; - } - if (try minify_options.getBooleanLoose(global, "identifiers")) |value| { options.minify_identifiers = value; + options.minify_whitespace = value; + } else if (minify_options.isObject()) { + if (try minify_options.getBooleanLoose(global, "whitespace")) |value| { + options.minify_whitespace = value; + } + if (try minify_options.getBooleanLoose(global, "syntax")) |value| { + options.minify_syntax = value; + } + if (try minify_options.getBooleanLoose(global, "identifiers")) |value| { + options.minify_identifiers = value; + } + } else { + return global.throwInvalidArguments("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..ec6428f02c6 100644 --- a/test/js/bun/http/bun-serve-args.test.ts +++ b/test/js/bun/http/bun-serve-args.test.ts @@ -670,3 +670,39 @@ describe("Bun.serve unix socket validation", () => { } }); }); + +describe("app.bundlerOptions validation", () => { + test("minify as non-boolean, non-object throws", () => { + expect(() => + // @ts-expect-error + serve({ app: { bundlerOptions: { ssr: { minify: 1225 } } } }), + ).toThrow("Expected minify to be a boolean or an object"); + expect(() => + // @ts-expect-error + serve({ app: { bundlerOptions: { client: { minify: "yes" } } } }), + ).toThrow("Expected minify to be a boolean or an object"); + }); + + test("minify as false does not crash", () => { + expect(() => + // @ts-expect-error + serve({ app: { bundlerOptions: { server: { minify: false } } } }), + ).not.toThrow("reached unreachable code"); + }); + + test("server/client/ssr as non-object throws", () => { + for (const key of ["server", "client", "ssr"]) { + expect(() => + // @ts-expect-error + serve({ app: { bundlerOptions: { [key]: 123 } } }), + ).toThrow("Expected bundler options to be an object"); + } + }); + + test("bundlerOptions as non-object throws", () => { + expect(() => + // @ts-expect-error + serve({ app: { bundlerOptions: 123 } }), + ).toThrow("'app.bundlerOptions' must be an object"); + }); +});