Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions cmd/esbuild/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ var helpText = func(colors logger.Colors) string {
--jsx-factory=... What to use for JSX instead of React.createElement
--jsx-fragment=... What to use for JSX instead of React.Fragment
--jsx=... Set to "preserve" to disable transforming JSX to JS
--jsx-runtime=... Set to "automatic" to use the new JSX transform
--jsx-development Use the automatic runtime in development mode
--jsx-import-source=... Override the package name for the automatic runtime
(default "react")
--keep-names Preserve "name" on functions and classes
--legal-comments=... Where to place legal comments (none | inline |
eof | linked | external, default eof when bundling
Expand Down
6 changes: 6 additions & 0 deletions internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,12 @@ func (s *scanner) maybeParseFile(
if len(resolveResult.JSXFragment) > 0 {
optionsClone.JSX.Fragment = config.DefineExpr{Parts: resolveResult.JSXFragment}
}
if resolveResult.JSX != config.TSJSXNone {
optionsClone.JSX.SetOptionsFromTSJSX(resolveResult.JSX)
}
if resolveResult.JSXImportSource != "" {
optionsClone.JSX.ImportSource = resolveResult.JSXImportSource
}
if resolveResult.UseDefineForClassFieldsTS != config.Unspecified {
optionsClone.UseDefineForClassFields = resolveResult.UseDefineForClassFieldsTS
}
Expand Down
82 changes: 82 additions & 0 deletions internal/bundler/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,88 @@ func TestJSXConstantFragments(t *testing.T) {
})
}

func TestJSXAutomaticImportsCommonJS(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.jsx": `
import {jsx, Fragment} from './custom-react'
console.log(<div jsx={jsx}/>, <><Fragment/></>)
`,
"/custom-react.js": `
module.exports = {}
`,
},
entryPaths: []string{"/entry.jsx"},
options: config.Options{
Mode: config.ModeBundle,
JSX: config.JSXOptions{
AutomaticRuntime: true,
},
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
"react/jsx-runtime": true,
}},
},
AbsOutputFile: "/out.js",
},
})
}

func TestJSXAutomaticImportsES6(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.jsx": `
import {jsx, Fragment} from './custom-react'
console.log(<div jsx={jsx}/>, <><Fragment/></>)
`,
"/custom-react.js": `
export function jsx() {}
export function Fragment() {}
`,
},
entryPaths: []string{"/entry.jsx"},
options: config.Options{
Mode: config.ModeBundle,
JSX: config.JSXOptions{
AutomaticRuntime: true,
},
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
"react/jsx-runtime": true,
}},
},
AbsOutputFile: "/out.js",
},
})
}

func TestJSXAutomaticSyntaxInJS(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
console.log(<div/>)
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
Mode: config.ModeBundle,
JSX: config.JSXOptions{
AutomaticRuntime: true,
},
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
"react/jsx-runtime": true,
}},
},
AbsOutputFile: "/out.js",
},
expectedScanLog: `entry.js: ERROR: The JSX syntax extension is not currently enabled
NOTE: The esbuild loader for this file is currently set to "js" but it must be set to "jsx" to be able to parse JSX syntax. ` +
`You can use 'Loader: map[string]api.Loader{".js": api.LoaderJSX}' to do that.
`,
})
}

func TestNodeModules(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
Expand Down
85 changes: 85 additions & 0 deletions internal/bundler/bundler_tsconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,91 @@ func TestTsConfigNestedJSX(t *testing.T) {
})
}

func TestTsConfigReactJSX(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/entry.tsx": `
console.log(<><div/><div/></>)
`,
"/Users/user/project/tsconfig.json": `
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "notreact"
}
}
`,
},
entryPaths: []string{"/Users/user/project/entry.tsx"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/Users/user/project/out.js",
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
"notreact/jsx-runtime": true,
}},
},
},
})
}

func TestTsConfigReactJSXDev(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/entry.tsx": `
console.log(<><div/><div/></>)
`,
"/Users/user/project/tsconfig.json": `
{
"compilerOptions": {
"jsx": "react-jsxdev"
}
}
`,
},
entryPaths: []string{"/Users/user/project/entry.tsx"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/Users/user/project/out.js",
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
"react/jsx-dev-runtime": true,
}},
},
},
})
}

func TestTsConfigReactJSXWithDevInMainConfig(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/entry.tsx": `
console.log(<><div/><div/></>)
`,
"/Users/user/project/tsconfig.json": `
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
`,
},
entryPaths: []string{"/Users/user/project/entry.tsx"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/Users/user/project/out.js",
JSX: config.JSXOptions{
Development: true,
},
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
"react/jsx-dev-runtime": true,
}},
},
},
})
}

func TestTsconfigJsonBaseUrl(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
Expand Down
42 changes: 42 additions & 0 deletions internal/bundler/snapshots/snapshots_default.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,48 @@ console.log(replace.test);
console.log(collide);
console.log(re_export);

================================================================================
TestJSXAutomaticImportsCommonJS
---------- /out.js ----------
// custom-react.js
var require_custom_react = __commonJS({
"custom-react.js"(exports, module) {
module.exports = {};
}
});

// entry.jsx
var import_custom_react = __toESM(require_custom_react());
console.log(/* @__PURE__ */ jsx2("div", {
jsx: import_custom_react.jsx
}), /* @__PURE__ */ jsx2(Fragment2, {
children: /* @__PURE__ */ jsx2(import_custom_react.Fragment, {})
}));
import {
Fragment as Fragment2,
jsx as jsx2
} from "react/jsx-runtime";

================================================================================
TestJSXAutomaticImportsES6
---------- /out.js ----------
// custom-react.js
function jsx() {
}
function Fragment() {
}

// entry.jsx
console.log(/* @__PURE__ */ jsx2("div", {
jsx
}), /* @__PURE__ */ jsx2(Fragment2, {
children: /* @__PURE__ */ jsx2(Fragment, {})
}));
import {
Fragment as Fragment2,
jsx as jsx2
} from "react/jsx-runtime";

================================================================================
TestJSXConstantFragments
---------- /out.js ----------
Expand Down
70 changes: 70 additions & 0 deletions internal/bundler/snapshots/snapshots_tsconfig.txt
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,76 @@ function fib(input) {
// Users/user/project/entry.ts
console.log(fib(10));

================================================================================
TestTsConfigReactJSX
---------- /Users/user/project/out.js ----------
// Users/user/project/entry.tsx
console.log(/* @__PURE__ */ jsxs(Fragment, {
children: [
/* @__PURE__ */ jsx("div", {}),
/* @__PURE__ */ jsx("div", {})
]
}));
import {
Fragment,
jsx,
jsxs
} from "notreact/jsx-runtime";

================================================================================
TestTsConfigReactJSXDev
---------- /Users/user/project/out.js ----------
// Users/user/project/entry.tsx
console.log(/* @__PURE__ */ jsxDEV(Fragment, {
children: [
/* @__PURE__ */ jsxDEV("div", {}, void 0, false, {
fileName: "Users/user/project/entry.tsx",
lineNumber: 2,
columnNumber: 18
}, this),
/* @__PURE__ */ jsxDEV("div", {}, void 0, false, {
fileName: "Users/user/project/entry.tsx",
lineNumber: 2,
columnNumber: 24
}, this)
]
}, void 0, true, {
fileName: "Users/user/project/entry.tsx",
lineNumber: 2,
columnNumber: 16
}, this));
import {
Fragment,
jsxDEV
} from "react/jsx-dev-runtime";

================================================================================
TestTsConfigReactJSXWithDevInMainConfig
---------- /Users/user/project/out.js ----------
// Users/user/project/entry.tsx
console.log(/* @__PURE__ */ jsxDEV(Fragment, {
children: [
/* @__PURE__ */ jsxDEV("div", {}, void 0, false, {
fileName: "Users/user/project/entry.tsx",
lineNumber: 2,
columnNumber: 18
}, this),
/* @__PURE__ */ jsxDEV("div", {}, void 0, false, {
fileName: "Users/user/project/entry.tsx",
lineNumber: 2,
columnNumber: 24
}, this)
]
}, void 0, true, {
fileName: "Users/user/project/entry.tsx",
lineNumber: 2,
columnNumber: 16
}, this));
import {
Fragment,
jsxDEV
} from "react/jsx-dev-runtime";

================================================================================
TestTsConfigWithStatementAlwaysStrictFalse
---------- /Users/user/project/out.js ----------
Expand Down
38 changes: 34 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,39 @@ import (
)

type JSXOptions struct {
Factory DefineExpr
Fragment DefineExpr
Parse bool
Preserve bool
Factory DefineExpr
Fragment DefineExpr
Parse bool
Preserve bool
AutomaticRuntime bool
ImportSource string
Development bool
}

type TSJSX uint8

const (
TSJSXNone TSJSX = iota
TSJSXPreserve
TSJSXReact
TSJSXReactJSX
TSJSXReactJSXDev
)

func (jsxOptions *JSXOptions) SetOptionsFromTSJSX(tsx TSJSX) {
switch tsx {
case TSJSXPreserve:
jsxOptions.Preserve = true
case TSJSXReact:
jsxOptions.AutomaticRuntime = false
jsxOptions.Development = false
case TSJSXReactJSX:
jsxOptions.AutomaticRuntime = true
// Don't set Development = false implicitly
case TSJSXReactJSXDev:
jsxOptions.AutomaticRuntime = true
jsxOptions.Development = true
}
}

type TSOptions struct {
Expand Down Expand Up @@ -276,6 +305,7 @@ type Options struct {
WriteToStdout bool

OmitRuntimeForTests bool
OmitJSXRuntimeForTests bool
UnusedImportFlagsTS UnusedImportFlagsTS
UseDefineForClassFields MaybeBool
ASCIIOnly bool
Expand Down
Loading