aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-06-20 19:29:20 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-20 19:29:20 -0700
commit6a1fbef8fdc8d2bb12a0124b5730d5682a4360d5 (patch)
tree68e7613eb244144800549b8ee7eda0fb2c2e4cb1
parent50064352346f48da74f66caca9d30b81a77c89b9 (diff)
downloadbun-6a1fbef8fdc8d2bb12a0124b5730d5682a4360d5.tar.gz
bun-6a1fbef8fdc8d2bb12a0124b5730d5682a4360d5.tar.zst
bun-6a1fbef8fdc8d2bb12a0124b5730d5682a4360d5.zip
record jsx factory symbols in classic mode (#3360)
* record jsx factory symbols * merge factory/fragment when more than one part * update test * use existing functions, use, `memberListToComponentsIfDifferent` * missing file * fix defaults
-rw-r--r--src/bundler.zig8
-rw-r--r--src/js_parser.zig8
-rw-r--r--src/options.zig4
-rw-r--r--test/bundler/bundler_jsx.test.ts76
-rw-r--r--test/bundler/esbuild/tsconfig.test.ts2
5 files changed, 93 insertions, 5 deletions
diff --git a/src/bundler.zig b/src/bundler.zig
index 3c796b576..fb0a11be7 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -506,7 +506,13 @@ pub const Bundler = struct {
switch (this.options.env.behavior) {
.prefix, .load_all => {
// Step 1. Load the project root.
- var dir: *Fs.FileSystem.DirEntry = ((this.resolver.readDirInfo(this.fs.top_level_dir) catch return) orelse return).getEntries(this.resolver.generation) orelse return;
+ const dir_info = this.resolver.readDirInfo(this.fs.top_level_dir) catch return orelse return;
+
+ if (dir_info.tsconfig_json) |tsconfig| {
+ this.options.jsx = tsconfig.mergeJSX(this.options.jsx);
+ }
+
+ var dir = dir_info.getEntries(this.resolver.generation) orelse return;
// Process always has highest priority.
const was_production = this.options.production;
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 51627a134..33a8cef6d 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -15096,6 +15096,10 @@ fn NewParser_(
if (e_.tag) |_tag| {
break :tagger p.visitExpr(_tag);
} else {
+ if (p.options.jsx.runtime == .classic) {
+ break :tagger p.jsxStringsToMemberExpression(expr.loc, p.options.jsx.fragment) catch unreachable;
+ }
+
break :tagger p.jsxImport(.Fragment, expr.loc);
}
};
@@ -15169,9 +15173,11 @@ fn NewParser_(
i += @intCast(usize, @boolToInt(args[i].data != .e_missing));
}
+ const target = p.jsxStringsToMemberExpression(expr.loc, p.options.jsx.factory) catch unreachable;
+
// Call createElement()
return p.newExpr(E.Call{
- .target = p.jsxImport(.createElement, expr.loc),
+ .target = target,
.args = ExprNodeList.init(args[0..i]),
// Enable tree shaking
.can_be_unwrapped_if_unused = !p.options.ignore_dce_annotations,
diff --git a/src/options.zig b/src/options.zig
index a39c9fc6c..08eb7b4b7 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -1048,8 +1048,8 @@ pub const JSX = struct {
}
pub const Defaults = struct {
- pub const Factory = &[_]string{"React.createElement"};
- pub const Fragment = &[_]string{"React.Fragment"};
+ pub const Factory = &[_]string{ "React", "createElement" };
+ pub const Fragment = &[_]string{ "React", "Fragment" };
pub const ImportSourceDev = "react/jsx-dev-runtime";
pub const ImportSource = "react/jsx-runtime";
pub const JSXFunction = "jsx";
diff --git a/test/bundler/bundler_jsx.test.ts b/test/bundler/bundler_jsx.test.ts
index a77eec0d0..5288701d6 100644
--- a/test/bundler/bundler_jsx.test.ts
+++ b/test/bundler/bundler_jsx.test.ts
@@ -323,4 +323,80 @@ describe("bundler", () => {
`,
},
});
+ itBundledDevAndProd("jsx/FactoryImport", {
+ todo: false,
+ files: {
+ "/index.jsx": /* js*/ `
+ import { h, fragment } from './jsx.ts';
+ const Fragment = 123;
+
+ import { print } from 'bun-test-helpers'
+ print([<div props={123}>Hello World</div>, <>Fragment</>])
+ `,
+ "/jsx.ts": /* ts */ `
+ export const h = () => 'hello factory';
+ export const fragment = () => 'hello fragment';
+ `,
+ ...helpers,
+ },
+ target: "bun",
+ jsx: {
+ runtime: "classic",
+ factory: "h",
+ fragment: "fragment",
+ },
+ run: {
+ stdout: `
+ [\"hello factory\",\"hello factory\"]
+ `,
+ },
+ onAfterBundle(api) {
+ expect(api.readFile("out.js")).toContain("h(fragment");
+ },
+ });
+ itBundledDevAndProd("jsx/FactoryImportExplicitReactDefault", {
+ todo: false,
+ files: {
+ "/index.jsx": /* js*/ `
+ import { print } from 'bun-test-helpers'
+ import * as React from 'react';
+ print([<div props={123}>Hello World</div>, <>Fragment</>])
+ `,
+ ...helpers,
+ },
+ target: "bun",
+ jsx: {
+ runtime: "classic",
+ factory: "React.createElement",
+ fragment: "React.Fragment",
+ },
+ onAfterBundle(api) {
+ expect(api.readFile("out.js")).toContain(" createElement");
+ expect(api.readFile("out.js")).toContain("(Fragment");
+ },
+ });
+ itBundledDevAndProd("jsx/FactoryImportExplicitReactDefaultExternal", {
+ todo: false,
+ files: {
+ "/index.jsx": /* js*/ `
+ import { print } from 'bun-test-helpers'
+ import * as React from 'react';
+ print([<div props={123}>Hello World</div>, <>Fragment</>])
+ `,
+ ...helpers,
+ },
+ target: "bun",
+ jsx: {
+ runtime: "classic",
+ factory: "React.createElement",
+ fragment: "React.Fragment",
+ },
+ external: ["react"],
+ onAfterBundle(api) {
+ const file = api.readFile("out.js");
+ expect(file).toContain("React.createElement");
+ expect(file).toContain("React.Fragment");
+ expect(file).toContain('import * as React from "react"');
+ },
+ });
});
diff --git a/test/bundler/esbuild/tsconfig.test.ts b/test/bundler/esbuild/tsconfig.test.ts
index e83a1eb23..b09a0d281 100644
--- a/test/bundler/esbuild/tsconfig.test.ts
+++ b/test/bundler/esbuild/tsconfig.test.ts
@@ -393,7 +393,7 @@ describe("bundler", () => {
onAfterBundle(api) {
api
.expectFile("/Users/user/project/out.js")
- .toContain(`console.log(c(F, null, c(\"div\", null), c(\"div\", null)));\n`);
+ .toContain(`console.log(R.c(R.F, null, R.c(\"div\", null), R.c(\"div\", null)));\n`);
},
});
itBundled("tsconfig/ReactJSXNotReact", {