aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bundler/bundle_v2.zig1
-rw-r--r--src/cli.zig4
-rw-r--r--src/js_parser.zig94
-rw-r--r--src/options.zig8
-rw-r--r--src/resolver/resolver.zig1
-rw-r--r--src/resolver/tsconfig_json.zig9
-rw-r--r--test/bundler/esbuild/tsconfig.test.ts206
7 files changed, 200 insertions, 123 deletions
diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig
index 0cbeec1f2..3873c6b12 100644
--- a/src/bundler/bundle_v2.zig
+++ b/src/bundler/bundle_v2.zig
@@ -666,7 +666,6 @@ pub const BundleV2 = struct {
var task = try this.graph.allocator.create(ParseTask);
task.* = ParseTask.init(&result, source_index, this);
task.loader = loader;
- task.jsx = this.bundler.options.jsx;
task.task.node.next = null;
task.tree_shaking = this.linker.options.tree_shaking;
diff --git a/src/cli.zig b/src/cli.zig
index 606e0a3cc..0ad948ac7 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -712,7 +712,7 @@ pub const Arguments = struct {
.factory = constStrToU8(jsx_factory orelse &default_factory),
.fragment = constStrToU8(jsx_fragment orelse &default_fragment),
.import_source = constStrToU8(jsx_import_source orelse &default_import_source),
- .runtime = if (jsx_runtime != null) try resolve_jsx_runtime(jsx_runtime.?) else Api.JsxRuntime.automatic,
+ .runtime = if (jsx_runtime) |runtime| try resolve_jsx_runtime(runtime) else Api.JsxRuntime.automatic,
.development = false,
.react_fast_refresh = react_fast_refresh,
};
@@ -721,7 +721,7 @@ pub const Arguments = struct {
.factory = constStrToU8(jsx_factory orelse opts.jsx.?.factory),
.fragment = constStrToU8(jsx_fragment orelse opts.jsx.?.fragment),
.import_source = constStrToU8(jsx_import_source orelse opts.jsx.?.import_source),
- .runtime = if (jsx_runtime != null) try resolve_jsx_runtime(jsx_runtime.?) else opts.jsx.?.runtime,
+ .runtime = if (jsx_runtime) |runtime| try resolve_jsx_runtime(runtime) else opts.jsx.?.runtime,
.development = false,
.react_fast_refresh = react_fast_refresh,
};
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 91631d1f6..a0f4defae 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -111,7 +111,7 @@ const JSXImport = enum {
jsxs: ?LocRef = null,
Fragment: ?LocRef = null,
createElement: ?LocRef = null,
- factory_name: []const u8 = "createElement",
+ factory_name: []const u8 = "React.createElement",
fragment_name: []const u8 = "Fragment",
pub fn get(this: *const Symbols, name: []const u8) ?Ref {
@@ -133,67 +133,31 @@ const JSXImport = enum {
};
}
- const Runtime = struct {
- pub const full: []const string = &[_]string{ "jsx", "jsxs" };
- pub const jsxs_: []const string = &[_]string{"jsxs"};
- pub const jsx_: []const string = &[_]string{"jsx"};
- };
-
- const DevRuntime = struct {
- pub const full: []const string = &[_]string{ "jsxDEV", "jsxs" };
- pub const jsxs_: []const string = &[_]string{"jsxs"};
- pub const jsx_: []const string = &[_]string{"jsxDEV"};
- };
- pub fn runtimeImportNames(this: *const Symbols) []const string {
+ pub fn runtimeImportNames(this: *const Symbols, buf: *[3]string) []const string {
+ var i: usize = 0;
if (this.jsxDEV != null) {
std.debug.assert(this.jsx == null); // we should never end up with this in the same file
-
- if (this.jsxs != null)
- return DevRuntime.full;
-
- return DevRuntime.jsx_;
+ buf[0] = "jsxDEV";
+ i += 1;
}
- if (this.jsx != null and this.jsxs != null)
- return Runtime.full;
-
- if (this.jsxs != null)
- return Runtime.jsxs_;
-
- if (this.jsx != null)
- return Runtime.jsx_;
-
- return &[_]string{};
- }
-
- const Legacy = struct {
- pub const full: []const string = &[_]string{ "createElement", "Fragment" };
- pub const createElement_: []const string = &[_]string{"createElement"};
- pub const Fragment_: []const string = &[_]string{"Fragment"};
- };
-
- pub fn legacyImportNames(this: *const Symbols, jsx: *const options.JSX.Pragma, buf: *[2]string) []const string {
- _ = jsx;
- if (this.Fragment != null and this.createElement != null) {
- buf[0..2].* = .{
- this.factory_name,
- this.fragment_name,
- };
- return buf[0..2];
+ if (this.jsx != null) {
+ std.debug.assert(this.jsxDEV == null); // we should never end up with this in the same file
+ buf[0] = "jsx";
+ i += 1;
}
- if (this.createElement != null) {
- buf[0] =
- this.factory_name;
- return buf[0..1];
+ if (this.jsxs != null) {
+ buf[i] = "jsxs";
+ i += 1;
}
if (this.Fragment != null) {
- buf[0] = this.fragment_name;
- return buf[0..1];
+ buf[i] = this.fragment_name;
+ i += 1;
}
- return &[_]string{};
+ return buf[0..i];
}
};
};
@@ -4199,10 +4163,9 @@ pub const Parser = struct {
}
// handle new way to do automatic JSX imports which fixes symbol collision issues
- if (p.options.jsx.parse and p.options.features.auto_import_jsx) {
- var legacy_import_names_buf = [2]string{ "", "" };
- const runtime_import_names = p.jsx_imports.runtimeImportNames();
- const legacy_import_names = p.jsx_imports.legacyImportNames(&p.options.jsx, &legacy_import_names_buf);
+ if (p.options.jsx.parse and p.options.features.auto_import_jsx and p.options.jsx.runtime == .automatic) {
+ var buf = [3]string{ "", "", "" };
+ const runtime_import_names = p.jsx_imports.runtimeImportNames(&buf);
if (runtime_import_names.len > 0) {
p.generateImportStmt(
@@ -4215,18 +4178,6 @@ pub const Parser = struct {
false,
) catch unreachable;
}
-
- if (legacy_import_names.len > 0) {
- p.generateImportStmt(
- p.options.jsx.classic_import_source,
- legacy_import_names,
- &before,
- &p.jsx_imports,
- null,
- "",
- false,
- ) catch unreachable;
- }
}
var parts_slice: []js_ast.Part = &([_]js_ast.Part{});
@@ -6466,7 +6417,7 @@ fn NewParser_(
}
if (p.lexer.jsx_pragma.jsxImportSource()) |import_source| {
- p.options.jsx.classic_import_source = options.JSX.Pragma.parsePackageName(import_source.text);
+ p.options.jsx.classic_import_source = import_source.text;
p.options.jsx.package_name = p.options.jsx.classic_import_source;
p.options.jsx.setImportSource(p.allocator);
}
@@ -6557,8 +6508,11 @@ fn NewParser_(
// "Foo.Bar.createElement" becomes:
// import { Bar } from 'foo';
// Usages become Bar.createElement
- if (p.options.jsx.fragment.len > 0)
- p.jsx_imports.fragment_name = p.options.jsx.fragment[if (p.options.jsx.fragment.len > 1) 1 else 0];
+
+ if (p.options.jsx.runtime == .classic) {
+ if (p.options.jsx.fragment.len > 0)
+ p.jsx_imports.fragment_name = p.options.jsx.fragment[if (p.options.jsx.fragment.len > 1) 1 else 0];
+ }
if (p.options.jsx.factory.len > 0)
p.jsx_imports.factory_name = p.options.jsx.factory[if (p.options.jsx.factory.len > 1) 1 else 0];
diff --git a/src/options.zig b/src/options.zig
index 9f5d3c552..6953a448f 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -965,7 +965,7 @@ pub const JSX = struct {
.{ "automatic", JSX.Runtime.automatic },
.{ "react", JSX.Runtime.classic },
.{ "react-jsx", JSX.Runtime.automatic },
- .{ "react-jsxDEV", JSX.Runtime.automatic },
+ .{ "react-jsxdev", JSX.Runtime.automatic },
.{ "solid", JSX.Runtime.solid },
});
@@ -1054,8 +1054,8 @@ pub const JSX = struct {
}
pub const Defaults = struct {
- pub const Factory = &[_]string{"createElement"};
- pub const Fragment = &[_]string{"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";
@@ -1116,7 +1116,7 @@ pub const JSX = struct {
pragma.runtime = jsx.runtime;
if (jsx.import_source.len > 0) {
- pragma.package_name = parsePackageName(pragma.importSource());
+ pragma.package_name = jsx.import_source;
pragma.setImportSource(allocator);
pragma.classic_import_source = pragma.package_name;
}
diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig
index 58f00c2af..068b22dc4 100644
--- a/src/resolver/resolver.zig
+++ b/src/resolver/resolver.zig
@@ -3758,6 +3758,7 @@ pub const Resolver = struct {
merged_config.base_url_for_paths = parent_config.base_url_for_paths;
}
merged_config.jsx = parent_config.mergeJSX(merged_config.jsx);
+ merged_config.jsx_flags.setUnion(parent_config.jsx_flags);
if (parent_config.preserve_imports_not_used_as_values) |value| {
merged_config.preserve_imports_not_used_as_values = value;
diff --git a/src/resolver/tsconfig_json.zig b/src/resolver/tsconfig_json.zig
index 7121e8741..7ec6b3e0b 100644
--- a/src/resolver/tsconfig_json.zig
+++ b/src/resolver/tsconfig_json.zig
@@ -157,11 +157,14 @@ pub const TSConfigJSON = struct {
if (compiler_opts.expr.asProperty("jsx")) |jsx_prop| {
if (jsx_prop.expr.asString(allocator)) |str| {
+ var str_lower = allocator.alloc(u8, str.len) catch unreachable;
+ defer allocator.free(str_lower);
+ _ = strings.copyLowercase(str, str_lower);
// we don't support "preserve" yet
- if (options.JSX.RuntimeMap.get(str)) |runtime| {
+ if (options.JSX.RuntimeMap.get(str_lower)) |runtime| {
result.jsx.runtime = runtime;
if (runtime == .automatic) {
- result.jsx.setProduction(!strings.contains(str, "jsxDEV"));
+ result.jsx.setProduction(!strings.contains(str_lower, "jsxdev"));
is_jsx_development = result.jsx.development;
result.jsx_flags.insert(.development);
}
@@ -179,7 +182,7 @@ pub const TSConfigJSON = struct {
result.jsx_flags.insert(.runtime);
}
- result.jsx.package_name = options.JSX.Pragma.parsePackageName(str);
+ result.jsx.package_name = str;
result.jsx.setImportSource(allocator);
result.jsx_flags.insert(.import_source);
}
diff --git a/test/bundler/esbuild/tsconfig.test.ts b/test/bundler/esbuild/tsconfig.test.ts
index b698ad07a..fc0072423 100644
--- a/test/bundler/esbuild/tsconfig.test.ts
+++ b/test/bundler/esbuild/tsconfig.test.ts
@@ -364,114 +364,234 @@ describe("bundler", () => {
"/Users/user/project/src/entry.ts": [`Could not resolve: "#/test". Maybe you need to "bun install"?`],
},
});
- return;
- itBundled("tsconfig/PathsTypeOnly", {
+ itBundled("tsconfig/JSX", {
// GENERATED
files: {
- "/Users/user/project/entry.ts": /* ts */ `
- import { fib } from "fib";
-
- console.log(fib(10));
- `,
- "/Users/user/project/node_modules/fib/index.js": /* js */ `
- export function fib(input) {
- if (input < 2) {
- return input;
- }
- return fib(input - 1) + fib(input - 2);
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/node_modules/react/jsx-dev-runtime.ts": `
+ export const Fragment = (props: { key?: string; children?: Child[] }): JSXNode => {
+ return new JSXFragmentNode('', {}, props.children || [])
+ }
+ export const jsx = (tag: string | JSXComponent, props: { key?: string; children?: Child[] }, ...children: Child[]): JSXNode => {
+ return new JSXNode(tag, props, children)
}
`,
- "/Users/user/project/fib-local.d.ts": `export function fib(input: number): number;`,
"/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "baseUrl": ".",
- "paths": {
- "fib": ["fib-local.d.ts"]
- }
+ "jsx": "react",
+ "jsxFactory": "R.c",
+ "jsxFragmentFactory": "R.F"
}
}
`,
},
+ outfile: "/Users/user/project/out.js",
+ external: ["react"],
+ onAfterBundle(api) {
+ api
+ .expectFile("/Users/user/project/out.js")
+ .toContain(`console.log(c(F, null, c(\"div\", null), c(\"div\", null)));\n`);
+ },
});
- itBundled("tsconfig/JSX", {
+ itBundled("tsconfig/ReactJSXNotReact", {
// GENERATED
files: {
"/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/node_modules/notreact/jsx-runtime.ts": `
+ export const Fragment = (props: { key?: string; children?: Child[] }): JSXNode => {
+ return new JSXFragmentNode('', {}, props.children || [])
+ }
+ export const jsx = (tag: string | JSXComponent, props: { key?: string; children?: Child[] }, ...children: Child[]): JSXNode => {
+ return new JSXNode(tag, props, children)
+ }
+ `,
"/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "jsxFactory": "R.c",
- "jsxFragmentFactory": "R.F"
+ "jsx": "react-jsx",
+ "jsxImportSource": "notreact"
}
}
`,
},
+ outfile: "/Users/user/project/out.js",
+ external: ["notreact"],
+ onAfterBundle(api) {
+ api.expectFile("/Users/user/project/out.js").toContain(`from "notreact/jsx-runtime`);
+ },
});
- itBundled("tsconfig/NestedJSX", {
+ itBundled("tsconfig/ReactJSXNotReactScoped", {
// GENERATED
files: {
- "/Users/user/project/entry.ts": /* ts */ `
- import factory from './factory'
- import fragment from './fragment'
- import both from './both'
- console.log(factory, fragment, both)
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/node_modules/@notreact/jsx/jsx-runtime.ts": `
+ export const Fragment = (props: { key?: string; children?: Child[] }): JSXNode => {
+ return new JSXFragmentNode('', {}, props.children || [])
+ }
+ export const jsx = (tag: string | JSXComponent, props: { key?: string; children?: Child[] }, ...children: Child[]): JSXNode => {
+ return new JSXNode(tag, props, children)
+ }
`,
- "/Users/user/project/factory/index.tsx": `export default <><div/><div/></>`,
- "/Users/user/project/factory/tsconfig.json": /* json */ `
+ "/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "jsxFactory": "h"
+ "jsx": "react-jsx",
+ "jsxImportSource": "@notreact/jsx"
}
}
`,
- "/Users/user/project/fragment/index.tsx": `export default <><div/><div/></>`,
- "/Users/user/project/fragment/tsconfig.json": /* json */ `
+ },
+ outfile: "/Users/user/project/out.js",
+ external: ["@notreact/jsx"],
+ onAfterBundle(api) {
+ api.expectFile("/Users/user/project/out.js").toContain(`from "@notreact/jsx/jsx-runtime`);
+ },
+ });
+ itBundled("tsconfig/ReactJSXDevNotReact", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/node_modules/notreact/jsx-dev-runtime.ts": `
+ export const Fragment = (props: { key?: string; children?: Child[] }): JSXNode => {
+ return new JSXFragmentNode('', {}, props.children || [])
+ }
+ export const jsx = (tag: string | JSXComponent, props: { key?: string; children?: Child[] }, ...children: Child[]): JSXNode => {
+ return new JSXNode(tag, props, children)
+ }
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "jsxFragmentFactory": "a.b"
+ "jsx": "react-jsxdev",
+ "jsxImportSource": "notreact"
}
}
`,
- "/Users/user/project/both/index.tsx": `export default <><div/><div/></>`,
- "/Users/user/project/both/tsconfig.json": /* json */ `
+ },
+ outfile: "/Users/user/project/out.js",
+ external: ["notreact"],
+ onAfterBundle(api) {
+ api.expectFile("/Users/user/project/out.js").toContain(`from "notreact/jsx-dev-runtime`);
+ },
+ });
+ itBundled("tsconfig/ReactJSXDev", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/node_modules/react/jsx-dev-runtime.ts": `
+ export const Fragment = (props: { key?: string; children?: Child[] }): JSXNode => {
+ return new JSXFragmentNode('', {}, props.children || [])
+ }
+ export const jsx = (tag: string | JSXComponent, props: { key?: string; children?: Child[] }, ...children: Child[]): JSXNode => {
+ return new JSXNode(tag, props, children)
+ }
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "jsxFactory": "R.c",
- "jsxFragmentFactory": "R.F"
+ "jsx": "react-jsxdev"
}
}
`,
},
+ external: ["react"],
+ outfile: "/Users/user/project/out.js",
+ onAfterBundle(api) {
+ api.expectFile("/Users/user/project/out.js").toContain(`from "react/jsx-dev-runtime`);
+ },
});
itBundled("tsconfig/ReactJSX", {
// GENERATED
files: {
"/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/node_modules/react/jsx-runtime.ts": `
+ export const Fragment = (props: { key?: string; children?: Child[] }): JSXNode => {
+ return new JSXFragmentNode('', {}, props.children || [])
+ }
+ export const jsx = (tag: string | JSXComponent, props: { key?: string; children?: Child[] }, ...children: Child[]): JSXNode => {
+ return new JSXNode(tag, props, children)
+ }
+ `,
"/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "jsx": "react-jsx",
- "jsxImportSource": "notreact"
+ "jsx": "react-jsx"
}
}
`,
},
+ external: ["react"],
outfile: "/Users/user/project/out.js",
+ onAfterBundle(api) {
+ api.expectFile("/Users/user/project/out.js").toContain(`from "react/jsx-runtime`);
+ },
});
- itBundled("tsconfig/ReactJSXDev", {
+ return;
+ itBundled("tsconfig/PathsTypeOnly", {
// GENERATED
files: {
- "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/entry.ts": /* ts */ `
+ import { fib } from "fib";
+
+ console.log(fib(10));
+ `,
+ "/Users/user/project/node_modules/fib/index.js": /* js */ `
+ export function fib(input) {
+ if (input < 2) {
+ return input;
+ }
+ return fib(input - 1) + fib(input - 2);
+ }
+ `,
+ "/Users/user/project/fib-local.d.ts": `export function fib(input: number): number;`,
"/Users/user/project/tsconfig.json": /* json */ `
{
"compilerOptions": {
- "jsx": "react-jsxdev"
+ "baseUrl": ".",
+ "paths": {
+ "fib": ["fib-local.d.ts"]
+ }
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/NestedJSX", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.ts": /* ts */ `
+ import factory from './factory'
+ import fragment from './fragment'
+ import both from './both'
+ console.log(factory, fragment, both)
+ `,
+ "/Users/user/project/factory/index.tsx": `export default <><div/><div/></>`,
+ "/Users/user/project/factory/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "h"
+ }
+ }
+ `,
+ "/Users/user/project/fragment/index.tsx": `export default <><div/><div/></>`,
+ "/Users/user/project/fragment/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFragmentFactory": "a.b"
+ }
+ }
+ `,
+ "/Users/user/project/both/index.tsx": `export default <><div/><div/></>`,
+ "/Users/user/project/both/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "R.c",
+ "jsxFragmentFactory": "R.F"
}
}
`,
},
- outfile: "/Users/user/project/out.js",
});
itBundled("tsconfig/ReactJSXWithDevInMainConfig", {
// GENERATED