Repository: gohugoio/hugo (language: go)

Issue:
`js.Build`'s `externals` option does not work for /assets
### Description and steps to reproduce

Under `assets`, I have a JS file `script.js` with contents
```js
import '/external.js';
```
The file `external.js` has contents
```js
console.log('This file should not be bundled!');
```
In my template, the script is included as follows:
```html
{{- /* Make sure the resource "external.js" is published. */}}
{{- (resources.Get "external.js").Publish }}

{{- $script := resources.Get "script.js" }}
{{- $opts := dict "format" "esm"
                  "externals" (slice "/external.js")
}}
{{- $script = $script | js.Build $opts }}
  <script type="module" src="{{ $script.Permalink }}"></script>
```

### Actual behaviour

The `externals` option seems to have no effect, and the file `external.js` is included in the bundled `script.js`. The bundled file looks as follows:
```js
// ns-hugo-imp:/home/rnwst/src/hugo-test/assets/external.js
console.log("This file should not be bundled!");

```

### Expected behaviour

`external.js` should not be bundled, and the import statement in `script.js` should be left untouched.

### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.140.0+extended linux/amd64 BuildDate=unknown
</pre>



Discussion / hints:
I created a reproducing example here: https://github.com/rnwst/hugo-testing/tree/hugo-github-issue-13183
I think I understand what's happening, and I suspect that this works fine for JS packages living in `node_modules`.

Fix patch:
diff --git a/internal/js/esbuild/resolve.go b/internal/js/esbuild/resolve.go
index ac0010da92e..8ceec97efc6 100644
--- a/internal/js/esbuild/resolve.go
+++ b/internal/js/esbuild/resolve.go
@@ -167,6 +167,17 @@ func createBuildPlugins(rs *resources.Spec, assetsResolver *fsResolver, depsMana
 			}
 		}
 
+		for _, ext := range opts.Externals {
+			// ESBuild will do a more thorough check for packages resolved in node_modules,
+			// but we need to make sure that we don't try to resolve these in the /assets folder.
+			if ext == impPath {
+				return api.OnResolveResult{
+					Path:     impPath,
+					External: true,
+				}, nil
+			}
+		}
+
 		if opts.ImportOnResolveFunc != nil {
 			if s := opts.ImportOnResolveFunc(impPath, args); s != "" {
 				return api.OnResolveResult{Path: s, Namespace: NsHugoImportResolveFunc}, nil


Test patch:
diff --git a/resources/resource_transformers/js/js_integration_test.go b/resources/resource_transformers/js/js_integration_test.go
index c62312ef566..9cee19a8659 100644
--- a/resources/resource_transformers/js/js_integration_test.go
+++ b/resources/resource_transformers/js/js_integration_test.go
@@ -391,3 +391,32 @@ class A {}
 		}).Build()
 	b.AssertFileContent("public/js/main.js", "__decorateClass")
 }
+
+// Issue 13183.
+func TestExternalsInAssets(t *testing.T) {
+	files := `
+-- assets/js/util1.js --
+export function hello1() {
+	return 'abcd';
+}
+-- assets/js/util2.js --
+export function hello2() {
+	return 'efgh';
+}
+-- assets/js/main.js --
+import { hello1 } from './util1.js';
+import { hello2 } from './util2.js';
+
+hello1();
+hello2();
+-- layouts/index.html --
+Home.
+{{ $js := resources.Get "js/main.js" | js.Build (dict "externals" (slice "./util1.js")) }}
+{{ $js.Publish }}
+`
+
+	b := hugolib.Test(t, files, hugolib.TestOptOsFs())
+
+	b.AssertFileContent("public/js/main.js", "efgh")
+	b.AssertFileContent("public/js/main.js", "! abcd")
+}