Skip to content

Commit

Permalink
js: Add Inject config option
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Jan 22, 2021
1 parent e19a046 commit af84b7c
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 33 deletions.
3 changes: 3 additions & 0 deletions docs/content/en/hugo-pipes/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
8 changes: 5 additions & 3 deletions hugolib/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
`)

Expand All @@ -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;
`)
}
23 changes: 23 additions & 0 deletions resources/resource_transformers/js/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"

Expand Down Expand Up @@ -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 {
Expand Down
72 changes: 42 additions & 30 deletions resources/resource_transformers/js/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"path/filepath"
"strings"

"github.com/spf13/afero"

"github.com/pkg/errors"

"github.com/evanw/esbuild/pkg/api"
Expand Down Expand Up @@ -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{}

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit af84b7c

Please sign in to comment.