From 554aa58db60000e7d375ffcdbd0143ac1bedf610 Mon Sep 17 00:00:00 2001 From: Benjamin Altpeter Date: Thu, 22 Feb 2024 17:58:22 +0100 Subject: [PATCH] js: Support JSX and JSXImportSourceOptions Fixes #12118 --- docs/content/en/functions/js/Build.md | 23 ++++++++++++++++++ docs/content/en/hugo-pipes/js.md | 23 ++++++++++++++++++ resources/resource_transformers/js/options.go | 24 +++++++++++++++++++ .../resource_transformers/js/options_test.go | 14 +++++++++++ 4 files changed, 84 insertions(+) diff --git a/docs/content/en/functions/js/Build.md b/docs/content/en/functions/js/Build.md index 835785486..de9097a67 100644 --- a/docs/content/en/functions/js/Build.md +++ b/docs/content/en/functions/js/Build.md @@ -111,6 +111,29 @@ format sourceMap : (`string`) Whether to generate `inline` or `external` source maps from esbuild. External source maps will be written to the target with the output file name + ".map". Input source maps can be read from js.Build and node modules and combined into the output source maps. By default, source maps are not created. +JSX {{< new-in 0.124.0 >}} +: (`string`) How to handle/transform JSX syntax. One of: `transform`, `preserve`, `automatic`. Default is `transform`. Notably, the `automatic` transform was introduced in React 17+ and will cause the necessary JSX helper functions to be imported automatically. See https://esbuild.github.io/api/#jsx + +JSXImportSource {{< new-in 0.124.0 >}} +: (`string`) Which library to use to automatically import its JSX helper functions from. This only works if `JSX` is set to `automatic`. The specified library needs to be installed through NPM and expose certain exports. See https://esbuild.github.io/api/#jsx-import-source + +The combination of `JSX` and `JSXImportSource` is helpful if you want to use a non-React JSX library like Preact, e.g.: + +```go-html-template +{{ $js := resources.Get "js/main.jsx" | js.Build (dict "JSX" "automatic" "JSXImportSource" "preact") }} +``` + +With the above, you can use Preact components and JSX without having to manually import `h` and `Fragment` every time: + +```jsx +import { render } from 'preact'; + +const App = () => <>Hello world!; + +const container = document.getElementById('app'); +if (container) render(, container); +``` + ### Import JS code from /assets `js.Build` has full support for the virtual union file system in [Hugo Modules](/hugo-modules/). You can see some simple examples in this [test project](https://github.com/gohugoio/hugoTestProjectJSModImports), but in short this means that you can do this: diff --git a/docs/content/en/hugo-pipes/js.md b/docs/content/en/hugo-pipes/js.md index 9d2bbbd82..8a2e2f40e 100644 --- a/docs/content/en/hugo-pipes/js.md +++ b/docs/content/en/hugo-pipes/js.md @@ -95,6 +95,29 @@ format sourceMap : (`string`) Whether to generate `inline` or `external` source maps from esbuild. External source maps will be written to the target with the output file name + ".map". Input source maps can be read from js.Build and node modules and combined into the output source maps. By default, source maps are not created. +JSX {{< new-in 0.124.0 >}} +: (`string`) How to handle/transform JSX syntax. One of: `transform`, `preserve`, `automatic`. Default is `transform`. Notably, the `automatic` transform was introduced in React 17+ and will cause the necessary JSX helper functions to be imported automatically. See https://esbuild.github.io/api/#jsx + +JSXImportSource {{< new-in 0.124.0 >}} +: (`string`) Which library to use to automatically import its JSX helper functions from. This only works if `JSX` is set to `automatic`. The specified library needs to be installed through NPM and expose certain exports. See https://esbuild.github.io/api/#jsx-import-source + +The combination of `JSX` and `JSXImportSource` is helpful if you want to use a non-React JSX library like Preact, e.g.: + +```go-html-template +{{ $js := resources.Get "js/main.jsx" | js.Build (dict "JSX" "automatic" "JSXImportSource" "preact") }} +``` + +With the above, you can use Preact components and JSX without having to manually import `h` and `Fragment` every time: + +```jsx +import { render } from 'preact'; + +const App = () => <>Hello world!; + +const container = document.getElementById('app'); +if (container) render(, container); +``` + ### Import JS code from /assets `js.Build` has full support for the virtual union file system in [Hugo Modules](/hugo-modules/). You can see some simple examples in this [test project](https://github.com/gohugoio/hugoTestProjectJSModImports), but in short this means that you can do this: diff --git a/resources/resource_transformers/js/options.go b/resources/resource_transformers/js/options.go index 1c29ad67c..7de88638c 100644 --- a/resources/resource_transformers/js/options.go +++ b/resources/resource_transformers/js/options.go @@ -86,6 +86,14 @@ type Options struct { // What to use instead of React.Fragment. JSXFragment string + // What to do about JSX syntax. + // See https://esbuild.github.io/api/#jsx + JSX string + + // Which library to use to automatically import JSX helper functions from. Only works if JSX is set to automatic. + // See https://esbuild.github.io/api/#jsx-import-source + JSXImportSource string + // There is/was a bug in WebKit with severe performance issue with the tracking // of TDZ checks in JavaScriptCore. // @@ -375,6 +383,19 @@ func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) { return } + var jsx api.JSX + switch opts.JSX { + case "", "transform": + jsx = api.JSXTransform + case "preserve": + jsx = api.JSXPreserve + case "automatic": + jsx = api.JSXAutomatic + default: + err = fmt.Errorf("unsupported jsx type: %q", opts.JSX) + return + } + var defines map[string]string if opts.Defines != nil { defines = maps.ToStringMapString(opts.Defines) @@ -416,6 +437,9 @@ func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) { JSXFactory: opts.JSXFactory, JSXFragment: opts.JSXFragment, + JSX: jsx, + JSXImportSource: opts.JSXImportSource, + Tsconfig: opts.tsConfig, // Note: We're not passing Sourcefile to ESBuild. diff --git a/resources/resource_transformers/js/options_test.go b/resources/resource_transformers/js/options_test.go index b8b031b81..a49b174ae 100644 --- a/resources/resource_transformers/js/options_test.go +++ b/resources/resource_transformers/js/options_test.go @@ -135,6 +135,20 @@ func TestToBuildOptions(t *testing.T) { Loader: api.LoaderJS, }, }) + + opts, err = toBuildOptions(Options{mediaType: media.Builtin.JavascriptType, + JSX: "automatic", JSXImportSource: "preact"}) + c.Assert(err, qt.IsNil) + c.Assert(opts, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + JSX: api.JSXAutomatic, + JSXImportSource: "preact", + }) } func TestResolveComponentInAssets(t *testing.T) {