Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 65 additions & 34 deletions src/bundler/bundle_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2130,7 +2130,9 @@ pub mod bv2_impl {
&import_record.source_file,
&import_record.specifier,
) {
let file_map_result = _file_map_result;
let mut file_map_result = _file_map_result;
// SAFETY: see `transpiler` note above.
file_map_result.jsx = unsafe { &(*transpiler).options.jsx }.clone();
let mut path_primary = file_map_result.path_pair.primary;
// reshaped for borrowck — `get_or_put` borrows `*self` mutably via
// `self.graph`; capture the slot as `*mut u32` so subsequent `self.*` calls
Expand Down Expand Up @@ -2532,13 +2534,13 @@ pub mod bv2_impl {
task.task.node.next = core::ptr::null_mut();
task.tree_shaking = self.linker.options.tree_shaking;
task.known_target = target;
if let Some(dev) = self
.transpiler_for_target(target)
.options
.force_node_env
.jsx_development_override()
{
let t = self.transpiler_for_target(target);
task.jsx.development = match t.options.force_node_env {
options::ForceNodeEnv::Development => true,
options::ForceNodeEnv::Production => false,
options::ForceNodeEnv::Unspecified => t.options.jsx.development,
};
task.jsx.development = dev;
}

// Handle onLoad plugins as entry points
Expand Down Expand Up @@ -2642,13 +2644,13 @@ pub mod bv2_impl {
task.tree_shaking = self.linker.options.tree_shaking;
task.is_entry_point = is_entry_point;
task.known_target = target;
if let Some(dev) = self
.transpiler_for_target(target)
.options
.force_node_env
.jsx_development_override()
{
let bundler = self.transpiler_for_target(target);
task.jsx.development = match bundler.options.force_node_env {
options::ForceNodeEnv::Development => true,
options::ForceNodeEnv::Production => false,
options::ForceNodeEnv::Unspecified => bundler.options.jsx.development,
};
task.jsx.development = dev;
}
Comment thread
claude[bot] marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.

// Handle onLoad plugins as entry points
Expand Down Expand Up @@ -3471,7 +3473,14 @@ pub mod bv2_impl {
// SAFETY: arena outlives the bundle pass; reborrow `*mut` as `&mut`.
let task: &mut ParseTask = self.arena_create(task_val);
task.loader = Some(loader);
task.jsx = self.transpiler_for_target(known_target).options.jsx.clone();
if let Some(dev) = self
.transpiler_for_target(known_target)
.options
.force_node_env
.jsx_development_override()
{
task.jsx.development = dev;
}
Comment thread
robobun marked this conversation as resolved.
task.task.node.next = core::ptr::null_mut();
task.io_task.node.next = core::ptr::null_mut();
task.tree_shaking = self.linker.options.tree_shaking;
Expand Down Expand Up @@ -3533,7 +3542,7 @@ pub mod bv2_impl {
let contents: &'static [u8] = unsafe { interned_slice(stored.contents()) };
// Compute borrow-heavy fields up front so the `&self` borrow taken by
// `arena()` doesn't overlap `&mut self` uses inside the literal.
let jsx = if known_target == Target::ServerComponentsSsr
let jsx_transpiler = if known_target == Target::ServerComponentsSsr
&& !self
.framework
.as_ref()
Expand All @@ -3543,10 +3552,18 @@ pub mod bv2_impl {
.unwrap()
.separate_ssr_graph
{
self.transpiler.options.jsx.clone()
&*self.transpiler
} else {
self.transpiler_for_target(known_target).options.jsx.clone()
&*self.transpiler_for_target(known_target)
};
let mut jsx = jsx_transpiler.options.jsx.clone();
if let Some(dev) = jsx_transpiler
.options
.force_node_env
.jsx_development_override()
{
jsx.development = dev;
}
let tree_shaking = self.linker.options.tree_shaking;
// SAFETY: arena (`self.graph.heap`) outlives the bundle pass; coerce the
// `&mut ParseTask` to `*mut` immediately so the `&self` borrow from
Expand Down Expand Up @@ -4646,11 +4663,29 @@ pub mod bv2_impl {
file: bun_sys::Fd::INVALID,
},
side_effects: bun_ast::SideEffects::HasSideEffects,
jsx: this
.transpiler_for_target(resolve.import_record.original_target)
.options
.jsx
.clone(),
jsx: {
let t = this.transpiler_for_target(
resolve.import_record.original_target,
);
let mut jsx = t.options.jsx.clone();
if path.namespace == b"file" {
let dir =
Fs::PathName::init(path.text).dir_with_trailing_slash();
if let Some(tsconfig) = t
.resolver
.read_dir_info_ignore_error(dir)
.and_then(|d| d.enclosing_tsconfig_json)
{
jsx = tsconfig.merge_jsx(jsx);
}
}
if let Some(dev) =
t.options.force_node_env.jsx_development_override()
{
jsx.development = dev;
}
jsx
},
source_index: bun_ast::Index::init(source_index.get()),
module_type: options::ModuleType::Unknown,
loader: Some(loader),
Expand Down Expand Up @@ -6044,13 +6079,11 @@ pub mod bv2_impl {
resolve_task.known_target = target;
// Use transpiler JSX options, applying force_node_env like the disk path does
resolve_task.jsx = transpiler.options.jsx.clone();
resolve_task.jsx.development = match transpiler.options.force_node_env {
options::ForceNodeEnv::Development => true,
options::ForceNodeEnv::Production => false,
options::ForceNodeEnv::Unspecified => {
transpiler.options.jsx.development
}
};
if let Some(dev) =
transpiler.options.force_node_env.jsx_development_override()
{
resolve_task.jsx.development = dev;
}
resolve_task.loader = Some(import_record_loader);
resolve_task.tree_shaking = transpiler.options.tree_shaking;
resolve_task.side_effects = bun_ast::SideEffects::HasSideEffects;
Expand Down Expand Up @@ -6413,11 +6446,9 @@ pub mod bv2_impl {
};

resolve_task.jsx = resolve_result.jsx.clone();
resolve_task.jsx.development = match transpiler.options.force_node_env {
options::ForceNodeEnv::Development => true,
options::ForceNodeEnv::Production => false,
options::ForceNodeEnv::Unspecified => transpiler.options.jsx.development,
};
if let Some(dev) = transpiler.options.force_node_env.jsx_development_override() {
resolve_task.jsx.development = dev;
}

resolve_task.loader = Some(import_record_loader);
resolve_task.tree_shaking = transpiler.options.tree_shaking;
Expand Down
20 changes: 12 additions & 8 deletions src/bundler/transpiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,14 +587,18 @@ impl<'a> Transpiler<'a> {
}
}

if is_development {
self.options.set_production(false);
self.resolver.opts.set_production(false);
self.options.force_node_env = options::ForceNodeEnv::Development;
self.resolver.opts.force_node_env = options::ForceNodeEnv::Development;
} else if is_production {
self.options.set_production(true);
self.resolver.opts.set_production(true);
if self.options.force_node_env == options::ForceNodeEnv::Unspecified {
if is_development {
self.options.set_production(false);
self.resolver.opts.set_production(false);
self.options.force_node_env = options::ForceNodeEnv::Development;
self.resolver.opts.force_node_env = options::ForceNodeEnv::Development;
} else if is_production {
self.options.set_production(true);
self.resolver.opts.set_production(true);
self.options.force_node_env = options::ForceNodeEnv::Production;
self.resolver.opts.force_node_env = options::ForceNodeEnv::Production;
}
}
Ok(())
}
Expand Down
11 changes: 11 additions & 0 deletions src/options_types/bundle_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ pub enum ForceNodeEnv {
Production,
}

impl ForceNodeEnv {
/// `Some(dev)` to override per-file tsconfig `jsx.development`; `None` keeps it.
pub fn jsx_development_override(self) -> Option<bool> {
match self {
ForceNodeEnv::Development => Some(true),
ForceNodeEnv::Production => Some(false),
ForceNodeEnv::Unspecified => None,
}
}
}

/// package.json `"type"` field.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/api/JSBundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub mod js_bundler {
pub outdir: OwnedString,
pub rootdir: OwnedString,
pub jsx: api::Jsx,
pub jsx_development_explicit: Option<bool>,
pub force_node_env: options::ForceNodeEnv,
pub code_splitting: bool,
pub minify: Minify,
Expand Down Expand Up @@ -191,6 +192,7 @@ pub mod js_bundler {
development: true, // Default to development mode like old Pragma
..Default::default()
},
jsx_development_explicit: None,
force_node_env: options::ForceNodeEnv::Unspecified,
code_splitting: false,
minify: Minify::default(),
Expand Down Expand Up @@ -717,6 +719,7 @@ pub mod js_bundler {
this.jsx.runtime = jsx_runtime_to_api(runtime.runtime);
if let Some(dev) = runtime.development {
this.jsx.development = dev;
this.jsx_development_explicit = Some(dev);
}
} else {
return Err(global_this.throw_invalid_arguments(format_args!(
Expand Down Expand Up @@ -744,6 +747,7 @@ pub mod js_bundler {

if let Some(dev) = jsx_value.get_boolean_loose(global_this, "development")? {
this.jsx.development = dev;
this.jsx_development_explicit = Some(dev);
}

if let Some(val) = jsx_value.get_boolean_loose(global_this, "sideEffects")? {
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/api/js_bundle_completion_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,14 @@ impl CompletionStruct for JSBundleCompletionTask {
.conditions
.append_slice(&[b"development"])?;
}
if let Some(dev) = config.jsx_development_explicit {
transpiler.options.jsx.development = dev;
transpiler.options.force_node_env = if dev {
options::ForceNodeEnv::Development
} else {
options::ForceNodeEnv::Production
};
}
Comment thread
robobun marked this conversation as resolved.
// `transpiler.env` is the dotenv loader installed by
// `Transpiler::init`; non-null and valid for `'a`.
transpiler.resolver.env_loader = NonNull::new(transpiler.env);
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/bake/bake_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,13 @@ impl Framework {
out.configure_defines()?;

out.options.jsx.development = mode == Mode::Development;
out.options.force_node_env = match mode {
Mode::Development => bun_bundler::options::ForceNodeEnv::Development,
Mode::ProductionDynamic | Mode::ProductionStatic => {
bun_bundler::options::ForceNodeEnv::Production
}
};
out.resolver.opts.force_node_env = out.options.force_node_env;

add_import_meta_defines(
&mut out.options.define,
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/bake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,13 @@ impl Framework {
out.configure_linker();
out.configure_defines()?;
out.options.jsx.development = mode == Mode::Development;
out.options.force_node_env = match mode {
Mode::Development => bun_bundler::options::ForceNodeEnv::Development,
Mode::ProductionDynamic | Mode::ProductionStatic => {
bun_bundler::options::ForceNodeEnv::Production
}
};
out.resolver.opts.force_node_env = out.options.force_node_env;

bake_body::add_import_meta_defines(
&mut out.options.define,
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/cli/build_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ impl BuildCommand {
if ctx.bundler_options.production {
this_transpiler.options.jsx.development = false;
this_transpiler.resolver.opts.jsx.development = false;
this_transpiler.options.force_node_env = options::ForceNodeEnv::Production;
this_transpiler.resolver.opts.force_node_env = options::ForceNodeEnv::Production;
}

match &ctx.debug.macros {
Expand Down
18 changes: 9 additions & 9 deletions test/bundler/bundler_jsx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,25 +900,25 @@ describe("bundler", () => {
const file = api.readFile("out.js");
// When sideEffects is true via tsconfig with automatic jsx: should NOT include /* @__PURE__ */ comments
expect(file).not.toContain("/* @__PURE__ */");
// tsconfig "react-jsx" selects the production automatic runtime; passing a
// jsx option to Bun.build must not flip it to the dev runtime.
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
"// @bun
// node_modules/react/jsx-dev-runtime.js
var $$typeof = Symbol.for("jsxdev");
function jsxDEV(type, props, key, source, self) {
// node_modules/react/jsx-runtime.js
var $$typeof = Symbol.for("jsx");
function jsx(type, props, key) {
return {
$$typeof,
type,
props,
key,
source,
self
key
};
}
var Fragment = Symbol.for("jsxdev.fragment");
var Fragment = Symbol.for("jsx.fragment");

// index.jsx
console.log(jsxDEV("a", {}, undefined, false, undefined, this));
console.log(jsxDEV(Fragment, {}, undefined, false, undefined, this));"
console.log(jsx("a", {}));
console.log(jsx(Fragment, {}));"
`);
},
});
Expand Down
Loading
Loading