Repository: gohugoio/hugo (language: go)

Issue:
Support more recent targets with js.Build / esbuild
Per [js.Build docs](https://gohugo.io/hugo-pipes/js/) the `target` option supports values including `es2015` through `es2020`, which refer to [esbuild's target option](https://esbuild.github.io/api/#target).

However, [esbuild currently supports](https://github.com/evanw/esbuild/blob/67cbf87a4909d87a902ca8c3b69ab5330defab0a/pkg/cli/cli_impl.go#L983) additional, more recent targets: `es2021`, `es2022`, `es2023`. I've tested that I can use them with raw esbuild, but they do not work when passed to js.Build in hugo.

Proposal is to add support for those targets to js.Build in hugo, for those who want to use a more recent target without jumping to the bleeding edge `esnext`.


Discussion / hints:

Fix patch:
diff --git a/go.mod b/go.mod
index ea2dcfdd320..64a08154a0f 100644
--- a/go.mod
+++ b/go.mod
@@ -25,7 +25,7 @@ require (
 	github.com/cli/safeexec v1.0.1
 	github.com/disintegration/gift v1.2.1
 	github.com/dustin/go-humanize v1.0.1
-	github.com/evanw/esbuild v0.20.2
+	github.com/evanw/esbuild v0.21.4
 	github.com/fatih/color v1.16.0
 	github.com/fortytw2/leaktest v1.3.0
 	github.com/frankban/quicktest v1.14.6
diff --git a/go.sum b/go.sum
index 923f6faea28..e61dd24a22f 100644
--- a/go.sum
+++ b/go.sum
@@ -185,6 +185,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/evanw/esbuild v0.20.2 h1:E4Y0iJsothpUCq7y0D+ERfqpJmPWrZpNybJA3x3I4p8=
 github.com/evanw/esbuild v0.20.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
+github.com/evanw/esbuild v0.21.4 h1:pe4SEQMoR1maEjhgWPEPWmUy11Jp6nidxd1mOvMrFFU=
+github.com/evanw/esbuild v0.21.4/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
diff --git a/resources/resource_transformers/js/options.go b/resources/resource_transformers/js/options.go
index 7de88638c16..8c271d032d7 100644
--- a/resources/resource_transformers/js/options.go
+++ b/resources/resource_transformers/js/options.go
@@ -342,6 +342,12 @@ func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) {
 		target = api.ES2019
 	case "es2020":
 		target = api.ES2020
+	case "es2021":
+		target = api.ES2021
+	case "es2022":
+		target = api.ES2022
+	case "es2023":
+		target = api.ES2023
 	default:
 		err = fmt.Errorf("invalid target: %q", opts.Target)
 		return


Test patch:
diff --git a/resources/resource_transformers/js/options_test.go b/resources/resource_transformers/js/options_test.go
index a49b174ae06..de20cbd05de 100644
--- a/resources/resource_transformers/js/options_test.go
+++ b/resources/resource_transformers/js/options_test.go
@@ -136,8 +136,10 @@ func TestToBuildOptions(t *testing.T) {
 		},
 	})
 
-	opts, err = toBuildOptions(Options{mediaType: media.Builtin.JavascriptType,
-		JSX: "automatic", JSXImportSource: "preact"})
+	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,
@@ -151,6 +153,36 @@ func TestToBuildOptions(t *testing.T) {
 	})
 }
 
+func TestToBuildOptionsTarget(t *testing.T) {
+	c := qt.New(t)
+
+	for _, test := range []struct {
+		target string
+		expect api.Target
+	}{
+		{"es2015", api.ES2015},
+		{"es2016", api.ES2016},
+		{"es2017", api.ES2017},
+		{"es2018", api.ES2018},
+		{"es2019", api.ES2019},
+		{"es2020", api.ES2020},
+		{"es2021", api.ES2021},
+		{"es2022", api.ES2022},
+		{"es2023", api.ES2023},
+		{"", api.ESNext},
+		{"esnext", api.ESNext},
+	} {
+		c.Run(test.target, func(c *qt.C) {
+			opts, err := toBuildOptions(Options{
+				Target:    test.target,
+				mediaType: media.Builtin.JavascriptType,
+			})
+			c.Assert(err, qt.IsNil)
+			c.Assert(opts.Target, qt.Equals, test.expect)
+		})
+	}
+}
+
 func TestResolveComponentInAssets(t *testing.T) {
 	c := qt.New(t)