Repository: gohugoio/hugo (language: go)

Issue:
js.Build vs  experimentalDecorators
In 0.113.0 I have a typescript component included by the partial
```
{{- $js := resources.Get "ts/index.ts" -}}
{{ $params := (dict "search_config" site.Params.search_config "is_production" hugo.IsProduction ) }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2020" "params" $params }}
{{ $js = $js | js.Build $opts }}
{{ if hugo.IsProduction }}
  {{ $js = $js | fingerprint }}
{{ end }}
<script
  src="{{ $js.RelPermalink }}"
  {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}
  type="module"></script>
```
that uses typescript decorators.  This component works as expected.

On upgrading, I get the following error:
```
ERROR js.Build failed: "/Users/luke/src/HarryPotter/assets/ts/components/HPGC/family.ts:14:2": Transforming JavaScript decorators to the configured target environment ("es2020") is not supported yet
```
repeated a number of times before the build finally fails, saying
```
Error: error building site: JSBUILD: failed to transform "ts/index.ts" (text/typescript): "/Users/luke/src/HarryPotter/assets/ts/components/HPGC/family.ts:11:0": Transforming JavaScript decorators to the configured target environment ("es2020") is not supported yet
```

reverting back to 0.113.0 and the build works again. 

### What version of Hugo are you using (`hugo version`)?

<pre>
hugo v0.114.1-e9b716ad9869b79e7c374bbdae4daf5ee6406bd4+extended darwin/arm64 BuildDate=2023-06-23T11:02:58Z VendorInfo=gohugoio
</pre>

and
<pre>
hugo v0.115.1-857374e69358f788bd31ddc55255c5c8e3dcfd80+extended darwin/arm64 BuildDate=2023-07-03T17:28:25Z VendorInfo=gohugoio
</pre>

I have tried with both.  The problem does not exist with 0.113.0. 
### Does this issue reproduce with the latest release?
Yes.


Discussion / hints:
In v0.113.0 we were using:
`github.com/evanw/esbuild="v0.17.19"`

In v0.114.0 we bumped to:
`github.com/evanw/esbuild="v0.18.11"`

See <https://github.com/evanw/esbuild/releases/tag/v0.18.0>

> This release deliberately contains backwards-incompatible changes
>
> Using experimental decorators now requires "experimentalDecorators": true (https://github.com/evanw/esbuild/issues/104)
>
> Previously esbuild would always compile decorators in TypeScript code using TypeScript's experimental decorator transform. Now that standard JavaScript decorators are close to being finalized, esbuild will now require you to use "experimentalDecorators": true to do this. This new requirement makes it possible for esbuild to introduce a transform for standard JavaScript decorators in TypeScript code in the future. Such a transform has not been implemented yet, however.


so I must be setting that in the wrong place.  My tsconfig has that line.

```
{
  "compilerOptions": {
    "target": "es2020",
    "module": "es2020",
    "lib": ["ES2020", "DOM"],
    "moduleResolution": "Node",
    "experimentalDecorators": true,
    "declaration": true,
    "useDefineForClassFields": false,
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "noEmit": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "alwaysStrict": true,
    "noFallthroughCasesInSwitch": false,
    "strictPropertyInitialization": false,
    "typeRoots": [
      "./node_modules/@types"
    ]
  },
  "exclude": [
    "node_modules",
    "cdk.out",
		"infrastructure/cdk.out"
  ]
}
```

it occurred to me that maybe it isn't using the tsconfig?  I tried three different ways of setting it.

```
{{- $js := resources.Get "ts/index.ts" -}}
{{ $params := (dict "search_config" site.Params.search_config "is_production" hugo.IsProduction ) }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2020" "experimentalDecorators" true "params" $params  }}
{{ $js = $js | js.Build $opts }}
{{ if hugo.IsProduction }}
  {{ $js = $js | fingerprint }}
{{ end }}
<script
  src="{{ $js.RelPermalink }}"
  {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}
  type="module"></script>
```

```
{{- $js := resources.Get "ts/index.ts" -}}
{{ $params := (dict "search_config" site.Params.search_config "is_production" hugo.IsProduction ) }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2020"  "params" $params "experimentalDecorators" true  }}
{{ $js = $js | js.Build $opts }}
{{ if hugo.IsProduction }}
  {{ $js = $js | fingerprint }}
{{ end }}
<script
  src="{{ $js.RelPermalink }}"
  {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}
  type="module"></script>
```

and

```
{{- $js := resources.Get "ts/index.ts" -}}
{{ $params := (dict "search_config" site.Params.search_config "is_production" hugo.IsProduction "experimentalDecorators" true  ) }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2020"  "params" $params }}
{{ $js = $js | js.Build $opts }}
{{ if hugo.IsProduction }}
  {{ $js = $js | fingerprint }}
{{ end }}
<script
  src="{{ $js.RelPermalink }}"
  {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}
  type="module"></script>
```

none of them change the result. 


Git bisects says baef235d5018621173b68db34e153be7d68fa982 is the first bad commit.

---

Minimal reproducible example:

```text
git clone --single-branch -b hugo-github-issue-11232 https://github.com/jmooring/hugo-testing hugo-github-issue-11232
cd hugo-github-issue-11232
hugo && grep decorateClass public/js/main.js | wc -l
```

Expected result: 2

Actual result in v0.114.0:

> Error: error building site: JSBUILD: failed to transform "main.ts" (text/typescript): "/home/jmooring/code/hugo-testing/assets/main.ts:5:0": Experimental decorators are not currently enabled

I try my best to read through the release notes/change sets of dependency updates, but esbuild has been really active lately, so I missed this one. 

>it occurred to me that maybe it isn't using the tsconfig? I

No, this is probably the build options that Hugo exposes -- and `experimentalDecorators` isn't in that list. You cannot currently set this, I'm afraid.
Ignore my last comment, this isn't part of the Go API. According to 

https://github.com/evanw/esbuild/commit/6f2c029071d4d791adb5ac77cead34284bc446b8

This should live in tsconfig.



Fix patch:
diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go
index 949cd4fcb0c..aa802d81ed4 100644
--- a/resources/resource_transformers/js/build.go
+++ b/resources/resource_transformers/js/build.go
@@ -86,6 +86,7 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
 	opts.resolveDir = t.c.rs.Cfg.BaseConfig().WorkingDir // where node_modules gets resolved
 	opts.contents = string(src)
 	opts.mediaType = ctx.InMediaType
+	opts.tsConfig = t.c.rs.ResolveJSConfigFile("tsconfig.json")
 
 	buildOptions, err := toBuildOptions(opts)
 	if err != nil {


Test patch:
diff --git a/resources/resource_transformers/js/integration_test.go b/resources/resource_transformers/js/integration_test.go
index 023569b8da8..0e311107b0b 100644
--- a/resources/resource_transformers/js/integration_test.go
+++ b/resources/resource_transformers/js/integration_test.go
@@ -48,7 +48,7 @@ export function hello3() {
 -- layouts/index.html --
 {{ $js := resources.Get "js/main.js" | js.Build }}
 JS Content:{{ $js.Content }}:End:
-	
+
 			`
 
 	c.Run("Basic", func(c *qt.C) {
@@ -90,9 +90,9 @@ disableKinds=["page", "section", "taxonomy", "term", "sitemap", "robotsTXT"]
 path="github.com/gohugoio/hugoTestProjectJSModImports"
 -- go.mod --
 module github.com/gohugoio/tests/testHugoModules
-		
+
 go 1.16
-		
+
 require github.com/gohugoio/hugoTestProjectJSModImports v0.10.0 // indirect
 -- package.json --
 {
@@ -100,7 +100,7 @@ require github.com/gohugoio/hugoTestProjectJSModImports v0.10.0 // indirect
 	"date-fns": "^2.16.1"
 	}
 }
-	
+
 `
 	b := hugolib.NewIntegrationTestBuilder(
 		hugolib.IntegrationTestConfig{
@@ -142,7 +142,7 @@ console.log("included");
 -- assets/js/main.js --
 import "./included";
 	import { toCamelCase } from "to-camel-case";
-	
+
 	console.log("main");
 	console.log("To camel:", toCamelCase("space case"));
 -- assets/js/myjsx.jsx --
@@ -222,7 +222,7 @@ import { hello1, hello2 } from './util1';
 hello1();
 hello2();
 -- assets/js/util1.js --
-/* Some 
+/* Some
 comments.
 */
 import { hello3 } from './util2';
@@ -239,7 +239,7 @@ export function hello3() {
 -- layouts/index.html --
 {{ $js := resources.Get "js/main.js" | js.Build }}
 JS Content:{{ $js.Content }}:End:
-	
+
 			`
 
 	c.Run("Import from main not found", func(c *qt.C) {
@@ -292,9 +292,9 @@ import 'imp3/foo.js';
 				}).Build()
 
 			expected := `
-IMPORT_SRC_DIR:imp1/index.js	
+IMPORT_SRC_DIR:imp1/index.js
 IMPORT_SRC_DIR:imp2/index.ts
-IMPORT_SRC_DIR:imp3/foo.ts		
+IMPORT_SRC_DIR:imp3/foo.ts
 `
 			expected = strings.ReplaceAll(expected, "IMPORT_SRC_DIR", importSrcDir)
 
@@ -340,7 +340,36 @@ console.log("Hello 2");
 License util1
 License util2
 Main license
-	
+
 	`)
 
 }
+
+// Issue #11232
+func TestTypeScriptExperimentalDecorators(t *testing.T) {
+	t.Parallel()
+	files := `
+-- hugo.toml --
+disableKinds = ['RSS','sitemap','taxonomy','term']
+-- tsconfig.json --
+{
+  "compilerOptions": {
+    "experimentalDecorators": true,
+  }
+}
+-- assets/ts/main.ts --
+function addFoo(target: any) {target.prototype.foo = 'bar'}
+@addFoo
+class A {}
+-- layouts/index.html --
+{{ $opts := dict "target" "es2020" "targetPath" "js/main.js" }}
+{{ (resources.Get "ts/main.ts" | js.Build $opts).Publish }}
+`
+	b := hugolib.NewIntegrationTestBuilder(
+		hugolib.IntegrationTestConfig{
+			T:           t,
+			NeedsOsFS:   true,
+			TxtarString: files,
+		}).Build()
+	b.AssertFileContent("public/js/main.js", "__decorateClass")
+}