Repository: gohugoio/hugo (language: go)

Issue:
Consist JS `import` behavior with ESBuild.
Discussed in https://discourse.gohugo.io/t/js-different-import-behavior-between-assets-and-node-modules/41884.


Discussion / hints:

Fix patch:
diff --git a/resources/resource_transformers/js/options.go b/resources/resource_transformers/js/options.go
index 2987f5915e1..71ae0457307 100644
--- a/resources/resource_transformers/js/options.go
+++ b/resources/resource_transformers/js/options.go
@@ -150,7 +150,7 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
 		for _, ext := range []string{".js", ".ts", ".tsx", ".jsx"} {
 			if strings.HasSuffix(impPath, ext) {
 				// Import of foo.js.js need the full name.
-				return nil
+				continue
 			}
 			if fi, err := fs.Stat(base + ext); err == nil {
 				return fi.(hugofs.FileMetaInfo).Meta()
@@ -163,12 +163,10 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
 
 	var m *hugofs.FileMeta
 
-	// See issue #8949.
 	// We need to check if this is a regular file imported without an extension.
 	// There may be ambigous situations where both foo.js and foo/index.js exists.
 	// This import order is in line with both how Node and ESBuild's native
 	// import resolver works.
-	// This was fixed in Hugo 0.88.
 
 	// It may be a regular file imported without an extension, e.g.
 	// foo or foo/index.
@@ -176,14 +174,17 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
 	if m != nil {
 		return m
 	}
-	if filepath.Base(impPath) == "index" {
+
+	base := filepath.Base(impPath)
+	if base == "index" {
+		// try index.esm.js etc.
 		m = findFirst(impPath + ".esm")
 		if m != nil {
 			return m
 		}
 	}
 
-	// Finally check the path as is.
+	// Check the path as is.
 	fi, err := fs.Stat(impPath)
 
 	if err == nil {
@@ -195,6 +196,8 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
 		} else {
 			m = fi.(hugofs.FileMetaInfo).Meta()
 		}
+	} else if strings.HasSuffix(base, ".js") {
+		m = findFirst(strings.TrimSuffix(impPath, ".js"))
 	}
 
 	return m


Test patch:
diff --git a/resources/resource_transformers/js/integration_test.go b/resources/resource_transformers/js/integration_test.go
index b9f466873d2..ef371248c59 100644
--- a/resources/resource_transformers/js/integration_test.go
+++ b/resources/resource_transformers/js/integration_test.go
@@ -259,3 +259,47 @@ JS Content:{{ $js.Content }}:End:
 	})
 
 }
+
+// See issue 10527.
+func TestImportHugoVsESBuild(t *testing.T) {
+	c := qt.New(t)
+
+	for _, importSrcDir := range []string{"node_modules", "assets"} {
+		c.Run(importSrcDir, func(c *qt.C) {
+			files := `
+-- IMPORT_SRC_DIR/imp1/index.js --
+console.log("IMPORT_SRC_DIR:imp1/index.js");
+-- IMPORT_SRC_DIR/imp2/index.ts --
+console.log("IMPORT_SRC_DIR:imp2/index.ts");
+-- IMPORT_SRC_DIR/imp3/foo.ts --
+console.log("IMPORT_SRC_DIR:imp3/foo.ts");
+-- assets/js/main.js --
+import 'imp1/index.js';
+import 'imp2/index.js';
+import 'imp3/foo.js';
+-- layouts/index.html --
+{{ $js := resources.Get "js/main.js" | js.Build }}
+{{ $js.RelPermalink }}
+			`
+
+			files = strings.ReplaceAll(files, "IMPORT_SRC_DIR", importSrcDir)
+
+			b := hugolib.NewIntegrationTestBuilder(
+				hugolib.IntegrationTestConfig{
+					T:           c,
+					NeedsOsFS:   true,
+					TxtarString: files,
+				}).Build()
+
+			expected := `
+IMPORT_SRC_DIR:imp1/index.js	
+IMPORT_SRC_DIR:imp2/index.ts
+IMPORT_SRC_DIR:imp3/foo.ts		
+`
+			expected = strings.ReplaceAll(expected, "IMPORT_SRC_DIR", importSrcDir)
+
+			b.AssertFileContent("public/js/main.js", expected)
+		})
+	}
+
+}