js: Add Inject config option
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 22 Jan 2021 16:07:23 +0000 (17:07 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 22 Jan 2021 22:43:44 +0000 (23:43 +0100)
Fixes #8164

docs/content/en/hugo-pipes/js.md
hugolib/js_test.go
resources/resource_transformers/js/build.go
resources/resource_transformers/js/options.go

index d03fc1c6d328e3e0495f7842277852a15bae8118..b57497d862e61b0efb2d4efb6d65934fbe620e61 100644 (file)
@@ -43,6 +43,9 @@ minify [bool]
 avoidTDZ {{< new-in "0.78.0" >}}
 : There is/was a bug in WebKit with severe performance issue with the tracking of TDZ checks in JavaScriptCore. Enabling this flag removes the TDZ and `const` assignment checks and may improve performance of larger JS codebases until the WebKit fix is in widespread use. See https://bugs.webkit.org/show_bug.cgi?id=199866
 
+inject [slice] {{< new-in "0.81.0" >}}
+: This option allows you to automatically replace a global variable with an import from another file. The path names must be relative to `assets`.  See https://esbuild.github.io/api/#inject
+
 shims {{< new-in "0.81.0" >}}
 : This option allows swapping out a component with another. A common use case is to load dependencies like React from a CDN  (with _shims_) when in production, but running with the full bundled `node_modules` dependency during development:
 
index 8ab0b970c04472a282dd39f598969ffe2cde5381..4bb8bf1778b7feabf1cb0b5c90319696b4e4780b 100644 (file)
@@ -187,7 +187,7 @@ path="github.com/gohugoio/hugoTestProjectJSModImports"
         
 go 1.15
         
-require github.com/gohugoio/hugoTestProjectJSModImports v0.8.0 // indirect
+require github.com/gohugoio/hugoTestProjectJSModImports v0.9.0 // indirect
 
 `)
 
@@ -214,10 +214,12 @@ var Hugo = "Rocks!";
 Hello3 from mod2. Date from date-fns: ${today}
 Hello from lib in the main project
 Hello5 from mod2.
-var myparam = "Hugo Rocks!";`)
+var myparam = "Hugo Rocks!";
+shim cwd
+`)
 
        // React JSX, verify the shimming.
-       b.AssertFileContent("public/js/like.js", `@v0.8.0/assets/js/shims/react.js
+       b.AssertFileContent("public/js/like.js", `@v0.9.0/assets/js/shims/react.js
 module.exports = window.ReactDOM;
 `)
 }
index ee60aa502ec5d5757fecaa6ddb3039a6eddb6144..bd126efdabf04f4d5496fc32cb32bc976c473745 100644 (file)
@@ -18,6 +18,7 @@ import (
        "io/ioutil"
        "os"
        "path"
+       "path/filepath"
        "regexp"
        "strings"
 
@@ -103,6 +104,28 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
                defer os.Remove(buildOptions.Outdir)
        }
 
+       if opts.Inject != nil {
+               // Resolve the absolute filenames.
+               for i, ext := range opts.Inject {
+                       impPath := filepath.FromSlash(ext)
+                       if filepath.IsAbs(impPath) {
+                               return errors.Errorf("inject: absolute paths not supported, must be relative to /assets")
+                       }
+
+                       m := resolveComponentInAssets(t.c.rs.Assets.Fs, impPath)
+
+                       if m == nil {
+                               return errors.Errorf("inject: file %q not found", ext)
+                       }
+
+                       opts.Inject[i] = m.Filename()
+
+               }
+
+               buildOptions.Inject = opts.Inject
+
+       }
+
        result := api.Build(buildOptions)
 
        if len(result.Errors) > 0 {
index a65e67ac0a9a2d8d58ef67c12ee25942063f9eb4..921e944d40259016ba8dcd1607c7afeb38863412 100644 (file)
@@ -20,6 +20,8 @@ import (
        "path/filepath"
        "strings"
 
+       "github.com/spf13/afero"
+
        "github.com/pkg/errors"
 
        "github.com/evanw/esbuild/pkg/api"
@@ -64,6 +66,11 @@ type Options struct {
        // External dependencies, e.g. "react".
        Externals []string
 
+       // This option allows you to automatically replace a global variable with an import from another file.
+       // The filenames must be relative to /assets.
+       // See https://esbuild.github.io/api/#inject
+       Inject []string
+
        // User defined symbols.
        Defines map[string]interface{}
 
@@ -137,6 +144,40 @@ func loaderFromFilename(filename string) api.Loader {
        return api.LoaderJS
 }
 
+func resolveComponentInAssets(fs afero.Fs, impPath string) hugofs.FileMeta {
+       findFirst := func(base string) hugofs.FileMeta {
+               // This is the most common sub-set of ESBuild's default extensions.
+               // 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 fi, err := fs.Stat(base + ext); err == nil {
+                               return fi.(hugofs.FileMetaInfo).Meta()
+                       }
+               }
+
+               // Not found.
+               return nil
+       }
+
+       var m hugofs.FileMeta
+
+       // First the path as is.
+       fi, err := fs.Stat(impPath)
+
+       if err == nil {
+               if fi.IsDir() {
+                       m = findFirst(filepath.Join(impPath, "index"))
+               } else {
+                       m = fi.(hugofs.FileMetaInfo).Meta()
+               }
+       } else {
+               // It may be a regular file imported without an extension.
+               m = findFirst(impPath)
+       }
+
+       return m
+}
+
 func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
        fs := c.rs.Assets
 
@@ -169,36 +210,7 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
                        impPath = filepath.Join(relDir, impPath)
                }
 
-               findFirst := func(base string) hugofs.FileMeta {
-                       // This is the most common sub-set of ESBuild's default extensions.
-                       // 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 fi, err := fs.Fs.Stat(base + ext); err == nil {
-                                       return fi.(hugofs.FileMetaInfo).Meta()
-                               }
-                       }
-
-                       // Not found.
-                       return nil
-               }
-
-               var m hugofs.FileMeta
-
-               // First the path as is.
-               fi, err := fs.Fs.Stat(impPath)
-
-               if err == nil {
-                       if fi.IsDir() {
-                               m = findFirst(filepath.Join(impPath, "index"))
-                       } else {
-                               m = fi.(hugofs.FileMetaInfo).Meta()
-                       }
-               } else {
-                       // It may be a regular file imported without an extension.
-                       m = findFirst(impPath)
-               }
-               //
+               m := resolveComponentInAssets(fs.Fs, impPath)
 
                if m != nil {
                        // Store the source root so we can create a jsconfig.json