mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Implement jsxSideEffects option for JSX dead code elimination control (#22298)
## Summary
Implements the `jsxSideEffects` option to control whether JSX elements
are marked as pure for dead code elimination, matching esbuild's
behavior from their TestJSXSideEffects test case.
## Features Added
- **tsconfig.json support**: `{"compilerOptions": {"jsxSideEffects":
true}}`
- **CLI flag support**: `--jsx-side-effects`
- **Dual runtime support**: Works with both classic
(`React.createElement`) and automatic (`jsx`/`jsxs`) JSX runtimes
- **Production/Development modes**: Works in both production and
development environments
- **Backward compatible**: Default value is `false` (maintains existing
behavior)
## Behavior
- **Default (`jsxSideEffects: false`)**: JSX elements marked with `/*
@__PURE__ */` comments (can be eliminated by bundlers)
- **When `jsxSideEffects: true`**: JSX elements NOT marked as pure
(always preserved)
## Example Usage
### tsconfig.json
```json
{
"compilerOptions": {
"jsxSideEffects": true
}
}
```
### CLI
```bash
bun build --jsx-side-effects
```
### Output Comparison
```javascript
// Input: console.log(<div>test</div>);
// Default (jsxSideEffects: false):
console.log(/* @__PURE__ */ React.createElement("div", null, "test"));
// With jsxSideEffects: true:
console.log(React.createElement("div", null, "test"));
```
## Implementation Details
- Added `side_effects: bool = false` field to `JSX.Pragma` struct
- Updated tsconfig.json parser to handle `jsxSideEffects` option
- Added CLI argument parsing for `--jsx-side-effects` flag
- Modified JSX element visiting logic to respect the `side_effects`
setting
- Updated API schema with proper encode/decode support
- Enhanced test framework to support the new JSX option
## Comprehensive Test Coverage (12 Tests)
### Core Functionality (4 tests)
- ✅ Classic JSX runtime with default behavior (includes `/* @__PURE__
*/`)
- ✅ Classic JSX runtime with `side_effects: true` (no `/* @__PURE__ */`)
- ✅ Automatic JSX runtime with default behavior (includes `/* @__PURE__
*/`)
- ✅ Automatic JSX runtime with `side_effects: true` (no `/* @__PURE__
*/`)
### Production Mode (4 tests)
- ✅ Classic JSX runtime in production with default behavior
- ✅ Classic JSX runtime in production with `side_effects: true`
- ✅ Automatic JSX runtime in production with default behavior
- ✅ Automatic JSX runtime in production with `side_effects: true`
### tsconfig.json Integration (4 tests)
- ✅ Default tsconfig.json behavior (automatic runtime, includes `/*
@__PURE__ */`)
- ✅ tsconfig.json with `jsxSideEffects: true` (automatic runtime, no `/*
@__PURE__ */`)
- ✅ tsconfig.json with `jsx: "react"` and `jsxSideEffects: true`
(classic runtime)
- ✅ tsconfig.json with `jsx: "react-jsx"` and `jsxSideEffects: true`
(automatic runtime)
### Snapshot Testing
All tests include inline snapshots demonstrating the exact output
differences, providing clear documentation of the expected behavior.
### Existing Compatibility
- ✅ All existing JSX tests continue to pass
- ✅ Cross-platform Zig compilation succeeds
## Closes
Fixes #22295
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -245,8 +245,8 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
|
||||
---
|
||||
|
||||
- `--jsx-side-effects`
|
||||
- n/a
|
||||
- JSX is always assumed to be side-effect-free
|
||||
- `--jsx-side-effects`
|
||||
- Controls whether JSX expressions are marked as `/* @__PURE__ */` for dead code elimination. Default is `false` (JSX marked as pure).
|
||||
|
||||
---
|
||||
|
||||
@@ -617,7 +617,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
|
||||
|
||||
- `jsxSideEffects`
|
||||
- `jsxSideEffects`
|
||||
- Not supported in JS API, configure in `tsconfig.json`
|
||||
- Controls whether JSX expressions are marked as pure for dead code elimination
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -246,6 +246,65 @@ The module from which the component factory function (`createElement`, `jsx`, `j
|
||||
|
||||
{% /table %}
|
||||
|
||||
### `jsxSideEffects`
|
||||
|
||||
By default, Bun marks JSX expressions as `/* @__PURE__ */` so they can be removed during bundling if they are unused (known as "dead code elimination" or "tree shaking"). Set `jsxSideEffects` to `true` to prevent this behavior.
|
||||
|
||||
{% table %}
|
||||
|
||||
- Compiler options
|
||||
- Transpiled output
|
||||
|
||||
---
|
||||
|
||||
- ```jsonc
|
||||
{
|
||||
"jsx": "react",
|
||||
// jsxSideEffects is false by default
|
||||
}
|
||||
```
|
||||
|
||||
- ```tsx
|
||||
// JSX expressions are marked as pure
|
||||
/* @__PURE__ */ React.createElement("div", null, "Hello");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
- ```jsonc
|
||||
{
|
||||
"jsx": "react",
|
||||
"jsxSideEffects": true,
|
||||
}
|
||||
```
|
||||
|
||||
- ```tsx
|
||||
// JSX expressions are not marked as pure
|
||||
React.createElement("div", null, "Hello");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
- ```jsonc
|
||||
{
|
||||
"jsx": "react-jsx",
|
||||
"jsxSideEffects": true,
|
||||
}
|
||||
```
|
||||
|
||||
- ```tsx
|
||||
// Automatic runtime also respects jsxSideEffects
|
||||
jsx("div", { children: "Hello" });
|
||||
```
|
||||
|
||||
{% /table %}
|
||||
|
||||
This option is also available as a CLI flag:
|
||||
|
||||
```bash
|
||||
$ bun build --jsx-side-effects
|
||||
```
|
||||
|
||||
### JSX pragma
|
||||
|
||||
All of these values can be set on a per-file basis using _pragmas_. A pragma is a special comment that sets a compiler option in a particular file.
|
||||
|
||||
@@ -799,6 +799,9 @@ pub const api = struct {
|
||||
/// import_source
|
||||
import_source: []const u8,
|
||||
|
||||
/// side_effects
|
||||
side_effects: bool = false,
|
||||
|
||||
pub fn decode(reader: anytype) anyerror!Jsx {
|
||||
var this = std.mem.zeroes(Jsx);
|
||||
|
||||
@@ -807,6 +810,7 @@ pub const api = struct {
|
||||
this.fragment = try reader.readValue([]const u8);
|
||||
this.development = try reader.readValue(bool);
|
||||
this.import_source = try reader.readValue([]const u8);
|
||||
this.side_effects = try reader.readValue(bool);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -816,6 +820,7 @@ pub const api = struct {
|
||||
try writer.writeValue(@TypeOf(this.fragment), this.fragment);
|
||||
try writer.writeInt(@as(u8, @intFromBool(this.development)));
|
||||
try writer.writeValue(@TypeOf(this.import_source), this.import_source);
|
||||
try writer.writeInt(@as(u8, @intFromBool(this.side_effects)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ pub fn VisitExpr(
|
||||
.target = if (runtime == .classic) target else p.jsxImport(.createElement, expr.loc),
|
||||
.args = ExprNodeList.init(args[0..i]),
|
||||
// Enable tree shaking
|
||||
.can_be_unwrapped_if_unused = if (!p.options.ignore_dce_annotations) .if_unused else .never,
|
||||
.can_be_unwrapped_if_unused = if (!p.options.ignore_dce_annotations and !p.options.jsx.side_effects) .if_unused else .never,
|
||||
.close_paren_loc = e_.close_tag_loc,
|
||||
}, expr.loc);
|
||||
}
|
||||
@@ -362,7 +362,7 @@ pub fn VisitExpr(
|
||||
.target = p.jsxImportAutomatic(expr.loc, is_static_jsx),
|
||||
.args = ExprNodeList.init(args),
|
||||
// Enable tree shaking
|
||||
.can_be_unwrapped_if_unused = if (!p.options.ignore_dce_annotations) .if_unused else .never,
|
||||
.can_be_unwrapped_if_unused = if (!p.options.ignore_dce_annotations and !p.options.jsx.side_effects) .if_unused else .never,
|
||||
.was_jsx_element = true,
|
||||
.close_paren_loc = e_.close_tag_loc,
|
||||
}, expr.loc);
|
||||
|
||||
@@ -71,6 +71,7 @@ pub const transpiler_params_ = [_]ParamType{
|
||||
clap.parseParam("--jsx-fragment <STR> Changes the function called when compiling JSX fragments") catch unreachable,
|
||||
clap.parseParam("--jsx-import-source <STR> Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: \"react\"") catch unreachable,
|
||||
clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable,
|
||||
clap.parseParam("--jsx-side-effects Treat JSX elements as having side effects (disable pure annotations)") catch unreachable,
|
||||
clap.parseParam("--ignore-dce-annotations Ignore tree-shaking annotations such as @__PURE__") catch unreachable,
|
||||
};
|
||||
pub const runtime_params_ = [_]ParamType{
|
||||
@@ -1120,6 +1121,7 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
const jsx_fragment = args.option("--jsx-fragment");
|
||||
const jsx_import_source = args.option("--jsx-import-source");
|
||||
const jsx_runtime = args.option("--jsx-runtime");
|
||||
const jsx_side_effects = args.flag("--jsx-side-effects");
|
||||
|
||||
if (cmd == .AutoCommand or cmd == .RunCommand) {
|
||||
// "run.silent" in bunfig.toml
|
||||
@@ -1166,6 +1168,7 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
.import_source = (jsx_import_source orelse &default_import_source),
|
||||
.runtime = if (jsx_runtime) |runtime| try resolve_jsx_runtime(runtime) else Api.JsxRuntime.automatic,
|
||||
.development = false,
|
||||
.side_effects = jsx_side_effects,
|
||||
};
|
||||
} else {
|
||||
opts.jsx = Api.Jsx{
|
||||
@@ -1174,6 +1177,7 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
.import_source = (jsx_import_source orelse opts.jsx.?.import_source),
|
||||
.runtime = if (jsx_runtime) |runtime| try resolve_jsx_runtime(runtime) else opts.jsx.?.runtime,
|
||||
.development = false,
|
||||
.side_effects = jsx_side_effects,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,6 +1242,7 @@ pub const JSX = struct {
|
||||
/// - tsconfig.json's `compilerOptions.jsx` (`react-jsx` or `react-jsxdev`)
|
||||
development: bool = true,
|
||||
parse: bool = true,
|
||||
side_effects: bool = false,
|
||||
|
||||
pub const ImportSource = struct {
|
||||
development: string = "react/jsx-dev-runtime",
|
||||
@@ -1380,6 +1381,7 @@ pub const JSX = struct {
|
||||
}
|
||||
|
||||
pragma.runtime = jsx.runtime;
|
||||
pragma.side_effects = jsx.side_effects;
|
||||
|
||||
if (jsx.import_source.len > 0) {
|
||||
pragma.package_name = jsx.import_source;
|
||||
|
||||
@@ -83,6 +83,10 @@ pub const TSConfigJSON = struct {
|
||||
out.development = this.jsx.development;
|
||||
}
|
||||
|
||||
if (this.jsx_flags.contains(.side_effects)) {
|
||||
out.side_effects = this.jsx.side_effects;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -226,6 +230,13 @@ pub const TSConfigJSON = struct {
|
||||
result.jsx_flags.insert(.import_source);
|
||||
}
|
||||
}
|
||||
// Parse "jsxSideEffects"
|
||||
if (compiler_opts.expr.asProperty("jsxSideEffects")) |jsx_prop| {
|
||||
if (jsx_prop.expr.asBool()) |val| {
|
||||
result.jsx.side_effects = val;
|
||||
result.jsx_flags.insert(.side_effects);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse "useDefineForClassFields"
|
||||
if (compiler_opts.expr.asProperty("useDefineForClassFields")) |use_define_value_prop| {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect } from "bun:test";
|
||||
import { normalizeBunSnapshot } from "harness";
|
||||
import { BundlerTestInput, itBundled } from "./expectBundled";
|
||||
|
||||
const helpers = {
|
||||
@@ -411,4 +412,387 @@ describe("bundler", () => {
|
||||
stdout: `{\n $$typeof: Symbol(hello_jsxDEV),\n type: \"div\",\n props: {\n children: \"Hello World\",\n },\n key: undefined,\n}`,
|
||||
},
|
||||
});
|
||||
|
||||
// Test for jsxSideEffects option - equivalent to esbuild's TestJSXSideEffects
|
||||
describe("jsxSideEffects", () => {
|
||||
itBundled("jsx/sideEffectsDefault", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "classic",
|
||||
factory: "React.createElement",
|
||||
fragment: "React.Fragment",
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// Default behavior: should include /* @__PURE__ */ comments
|
||||
expect(file).toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// index.jsx
|
||||
console.log(/* @__PURE__ */ React.createElement("a", null));
|
||||
console.log(/* @__PURE__ */ React.createElement(React.Fragment, null));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrue", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "classic",
|
||||
factory: "React.createElement",
|
||||
fragment: "React.Fragment",
|
||||
side_effects: true,
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(file).toContain("React.createElement");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// index.jsx
|
||||
console.log(React.createElement("a", null));
|
||||
console.log(React.createElement(React.Fragment, null));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
// Test automatic JSX runtime with side effects
|
||||
itBundled("jsx/sideEffectsDefaultAutomatic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// Default behavior: should include /* @__PURE__ */ comments
|
||||
expect(file).toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-dev-runtime.js
|
||||
var $$typeof = Symbol.for("jsxdev");
|
||||
function jsxDEV(type, props, key, source, self) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
source,
|
||||
self
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsxdev.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(/* @__PURE__ */ jsxDEV("a", {}, undefined, false, undefined, this));
|
||||
console.log(/* @__PURE__ */ jsxDEV(Fragment, {}, undefined, false, undefined, this));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrueAutomatic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "automatic",
|
||||
side_effects: true,
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-dev-runtime.js
|
||||
var $$typeof = Symbol.for("jsxdev");
|
||||
function jsxDEV(type, props, key, source, self) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
source,
|
||||
self
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsxdev.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(jsxDEV("a", {}, undefined, false, undefined, this));
|
||||
console.log(jsxDEV(Fragment, {}, undefined, false, undefined, this));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
// Test JSX production mode (non-development) with side effects
|
||||
itBundled("jsx/sideEffectsDefaultProductionClassic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "classic",
|
||||
factory: "React.createElement",
|
||||
fragment: "React.Fragment",
|
||||
},
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// Default behavior in production: should include /* @__PURE__ */ comments
|
||||
expect(file).toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// index.jsx
|
||||
console.log(/* @__PURE__ */ React.createElement("a", null));
|
||||
console.log(/* @__PURE__ */ React.createElement(React.Fragment, null));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrueProductionClassic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "classic",
|
||||
factory: "React.createElement",
|
||||
fragment: "React.Fragment",
|
||||
side_effects: true,
|
||||
},
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true in production: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(file).toContain("React.createElement");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// index.jsx
|
||||
console.log(React.createElement("a", null));
|
||||
console.log(React.createElement(React.Fragment, null));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsDefaultProductionAutomatic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// Default behavior in production: should include /* @__PURE__ */ comments
|
||||
expect(file).toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-runtime.js
|
||||
var $$typeof = Symbol.for("jsx");
|
||||
function jsx(type, props, key) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsx.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(/* @__PURE__ */ jsx("a", {}));
|
||||
console.log(/* @__PURE__ */ jsx(Fragment, {}));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrueProductionAutomatic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
jsx: {
|
||||
runtime: "automatic",
|
||||
side_effects: true,
|
||||
},
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true in production: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-runtime.js
|
||||
var $$typeof = Symbol.for("jsx");
|
||||
function jsx(type, props, key) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsx.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(jsx("a", {}));
|
||||
console.log(jsx(Fragment, {}));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
// Test tsconfig.json parsing for jsxSideEffects option
|
||||
itBundled("jsx/sideEffectsDefaultTsconfig", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
"/tsconfig.json": /* json */ `{"compilerOptions": {}}`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// Default behavior via tsconfig: should include /* @__PURE__ */ comments
|
||||
expect(file).toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-dev-runtime.js
|
||||
var $$typeof = Symbol.for("jsxdev");
|
||||
function jsxDEV(type, props, key, source, self) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
source,
|
||||
self
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsxdev.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(/* @__PURE__ */ jsxDEV("a", {}, undefined, false, undefined, this));
|
||||
console.log(/* @__PURE__ */ jsxDEV(Fragment, {}, undefined, false, undefined, this));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrueTsconfig", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
"/tsconfig.json": /* json */ `{"compilerOptions": {"jsxSideEffects": true}}`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true via tsconfig: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-dev-runtime.js
|
||||
var $$typeof = Symbol.for("jsxdev");
|
||||
function jsxDEV(type, props, key, source, self) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
source,
|
||||
self
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsxdev.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(jsxDEV("a", {}, undefined, false, undefined, this));
|
||||
console.log(jsxDEV(Fragment, {}, undefined, false, undefined, this));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrueTsconfigClassic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
"/tsconfig.json": /* json */ `{"compilerOptions": {"jsx": "react", "jsxSideEffects": true}}`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true via tsconfig with classic jsx: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(file).toContain("React.createElement");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// index.jsx
|
||||
console.log(React.createElement("a", null));
|
||||
console.log(React.createElement(React.Fragment, null));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("jsx/sideEffectsTrueTsconfigAutomatic", {
|
||||
files: {
|
||||
"/index.jsx": /* jsx */ `console.log(<a></a>); console.log(<></>);`,
|
||||
"/tsconfig.json": /* json */ `{"compilerOptions": {"jsx": "react-jsx", "jsxSideEffects": true}}`,
|
||||
...helpers,
|
||||
},
|
||||
target: "bun",
|
||||
onAfterBundle(api) {
|
||||
const file = api.readFile("out.js");
|
||||
// When jsxSideEffects is true via tsconfig with automatic jsx: should NOT include /* @__PURE__ */ comments
|
||||
expect(file).not.toContain("/* @__PURE__ */");
|
||||
expect(normalizeBunSnapshot(file)).toMatchInlineSnapshot(`
|
||||
"// @bun
|
||||
// node_modules/react/jsx-dev-runtime.js
|
||||
var $$typeof = Symbol.for("jsxdev");
|
||||
function jsxDEV(type, props, key, source, self) {
|
||||
return {
|
||||
$$typeof,
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
source,
|
||||
self
|
||||
};
|
||||
}
|
||||
var Fragment = Symbol.for("jsxdev.fragment");
|
||||
|
||||
// index.jsx
|
||||
console.log(jsxDEV("a", {}, undefined, false, undefined, this));
|
||||
console.log(jsxDEV(Fragment, {}, undefined, false, undefined, this));"
|
||||
`);
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -717,6 +717,7 @@ function expectBundled(
|
||||
jsx.factory && ["--jsx-factory", jsx.factory],
|
||||
jsx.fragment && ["--jsx-fragment", jsx.fragment],
|
||||
jsx.importSource && ["--jsx-import-source", jsx.importSource],
|
||||
jsx.side_effects && ["--jsx-side-effects"],
|
||||
dotenv && ["--env", dotenv],
|
||||
// metafile && `--manifest=${metafile}`,
|
||||
sourceMap && `--sourcemap=${sourceMap}`,
|
||||
@@ -760,6 +761,7 @@ function expectBundled(
|
||||
// jsx.preserve && "--jsx=preserve",
|
||||
jsx.factory && `--jsx-factory=${jsx.factory}`,
|
||||
jsx.fragment && `--jsx-fragment=${jsx.fragment}`,
|
||||
jsx.side_effects && `--jsx-side-effects`,
|
||||
env?.NODE_ENV !== "production" && `--jsx-dev`,
|
||||
entryNaming &&
|
||||
entryNaming !== "[dir]/[name].[ext]" &&
|
||||
|
||||
Reference in New Issue
Block a user