js: Fix import order for ./foo when both ./foo.js and ./foo/index.js exists
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 1 Sep 2021 07:07:29 +0000 (09:07 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 1 Sep 2021 08:47:41 +0000 (10:47 +0200)
This is in line with how both Node and ESBuild's native import resolver does it.

The ambiguous situations above were discovered trying to build AlpineJS v3.

Note that the above was never an issue if you used `./foo.js` and similar to import the component.

Fixes #8945

go.sum
resources/resource_transformers/js/options.go
resources/resource_transformers/js/options_test.go

diff --git a/go.sum b/go.sum
index 3922f6bc4b6e1ce5a5cc3b391f6992610925b61e..444547656af3bec0c82ee537fa7fa6f5693dd389 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -188,6 +188,12 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/evanw/esbuild v0.8.39 h1:/kOe+5zUXSzB2y2m/BxgNsQ5wpgbcGU2uE6MBVSleww=
+github.com/evanw/esbuild v0.8.39/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
+github.com/evanw/esbuild v0.10.2 h1:aTkCGE1R5YYhXl375VKQKtJmW85mnsGo15KW5nnYYIs=
+github.com/evanw/esbuild v0.10.2/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
+github.com/evanw/esbuild v0.11.23 h1:Y5I6OTABHBmOmt2cbRKrByW5sLdTrpaWhV2MihqLLiw=
+github.com/evanw/esbuild v0.11.23/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
 github.com/evanw/esbuild v0.12.24 h1:AlNPVfiY7tc6Di54tm6MngT2MpWVUleBc/Rg3wlr8rI=
 github.com/evanw/esbuild v0.12.24/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
index d1359ebda3707f794401af1ce405cc2f15e73598..9d2bb2e708637a4b2016d695b2099ad12942f6b8 100644 (file)
@@ -149,6 +149,10 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
                // We assume that imports of JSON, CSS etc. will be using their full
                // name with extension.
                for _, ext := range []string{".js", ".ts", ".tsx", ".jsx"} {
+                       if strings.HasSuffix(impPath, ext) {
+                               // Import of foo.js.js need the full name.
+                               return nil
+                       }
                        if fi, err := fs.Stat(base + ext); err == nil {
                                return fi.(hugofs.FileMetaInfo).Meta()
                        }
@@ -160,7 +164,21 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
 
        var m *hugofs.FileMeta
 
-       // First the path as is.
+       // 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.
+       m = findFirst(impPath)
+       if m != nil {
+               return m
+       }
+
+       // Finally check the path as is.
        fi, err := fs.Stat(impPath)
 
        if err == nil {
@@ -169,9 +187,6 @@ func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta {
                } else {
                        m = fi.(hugofs.FileMetaInfo).Meta()
                }
-       } else {
-               // It may be a regular file imported without an extension.
-               m = findFirst(impPath)
        }
 
        return m
index c178ee8c9553f8b24d9457c21c818d0fc8e48b01..e5b04e3550003201070f4cbb43469bf750ad4022 100644 (file)
 package js
 
 import (
+       "path/filepath"
        "testing"
 
+       "github.com/gohugoio/hugo/hugofs"
+
+       "github.com/spf13/afero"
+
        "github.com/gohugoio/hugo/media"
 
        "github.com/evanw/esbuild/pkg/api"
@@ -127,3 +132,48 @@ func TestToBuildOptions(t *testing.T) {
                },
        })
 }
+
+func TestResolveComponentInAssets(t *testing.T) {
+       c := qt.New(t)
+
+       for _, test := range []struct {
+               name    string
+               files   []string
+               impPath string
+               expect  string
+       }{
+               {"Basic, extension", []string{"foo.js", "bar.js"}, "foo.js", "foo.js"},
+               {"Basic, no extension", []string{"foo.js", "bar.js"}, "foo", "foo.js"},
+               {"Basic, no extension, typescript", []string{"foo.ts", "bar.js"}, "foo", "foo.ts"},
+               {"Not found", []string{"foo.js", "bar.js"}, "moo.js", ""},
+               {"Not found, double js extension", []string{"foo.js.js", "bar.js"}, "foo.js", ""},
+               {"Index file, folder only", []string{"foo/index.js", "bar.js"}, "foo", "foo/index.js"},
+               {"Index file, folder and index", []string{"foo/index.js", "bar.js"}, "foo/index", "foo/index.js"},
+               {"Index file, folder and index and suffix", []string{"foo/index.js", "bar.js"}, "foo/index.js", "foo/index.js"},
+
+               // Issue #8949
+               {"Check file before directory", []string{"foo.js", "foo/index.js"}, "foo", "foo.js"},
+       } {
+
+               c.Run(test.name, func(c *qt.C) {
+                       baseDir := "assets"
+                       mfs := afero.NewMemMapFs()
+
+                       for _, filename := range test.files {
+                               c.Assert(afero.WriteFile(mfs, filepath.Join(baseDir, filename), []byte("let foo='bar';"), 0777), qt.IsNil)
+                       }
+
+                       bfs := hugofs.DecorateBasePathFs(afero.NewBasePathFs(mfs, baseDir).(*afero.BasePathFs))
+
+                       got := resolveComponentInAssets(bfs, test.impPath)
+
+                       gotPath := ""
+                       if got != nil {
+                               gotPath = filepath.ToSlash(got.Path)
+                       }
+
+                       c.Assert(gotPath, qt.Equals, test.expect)
+               })
+
+       }
+}