Skip to content

Commit

Permalink
js: Misc fixes
Browse files Browse the repository at this point in the history
* Fix resolve of package.json deps in submodules
* Fix directory logic for writing assets/jsconfig.json

Fixes gohugoio#7924
Fixes gohugoio#7923
  • Loading branch information
bep committed Nov 4, 2020
1 parent cf6131d commit d50379c
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 61 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/bep/tmc v0.5.1
github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.0
github.com/evanw/esbuild v0.8.2
github.com/evanw/esbuild v0.8.3
github.com/fortytw2/leaktest v1.3.0
github.com/frankban/quicktest v1.11.1
github.com/fsnotify/fsnotify v1.4.9
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ github.com/evanw/esbuild v0.8.1 h1:AqGawd1vAh0l88ZzAyuG9/w4B3Hswt0wM5s05AYHYXo=
github.com/evanw/esbuild v0.8.1/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
github.com/evanw/esbuild v0.8.2 h1:pwvPPsU8dqwBLdPwBmETdp1ccpefC1l+8RKZD1PafcA=
github.com/evanw/esbuild v0.8.2/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
github.com/evanw/esbuild v0.8.3 h1:uPgAFhcGcNyMDrBnfUDcimt0N9AC9UsxeROkC8C27os=
github.com/evanw/esbuild v0.8.3/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q=
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
Expand Down
41 changes: 23 additions & 18 deletions hugolib/hugo_sites_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,26 +354,31 @@ func (h *HugoSites) postProcess() error {
// Write a jsconfig.json file to the project's /asset directory
// to help JS intellisense in VS Code etc.
if !h.ResourceSpec.BuildConfig.NoJSConfigInAssets && h.BaseFs.Assets.Dirs != nil {
m := h.BaseFs.Assets.Dirs[0].Meta()
assetsDir := m.Filename()
if strings.HasPrefix(assetsDir, h.ResourceSpec.WorkingDir) {
if jsConfig := h.ResourceSpec.JSConfigBuilder.Build(assetsDir); jsConfig != nil {
fi, err := h.BaseFs.Assets.Fs.Stat("")
if err != nil {
h.Log.Warnf("Failed to resolve jsconfig.json dir: %s", err)
} else {
m := fi.(hugofs.FileMetaInfo).Meta()
assetsDir := m.SourceRoot()
if strings.HasPrefix(assetsDir, h.ResourceSpec.WorkingDir) {
if jsConfig := h.ResourceSpec.JSConfigBuilder.Build(assetsDir); jsConfig != nil {

b, err := json.MarshalIndent(jsConfig, "", " ")
if err != nil {
h.Log.Warnf("Failed to create jsconfig.json: %s", err)
b, err := json.MarshalIndent(jsConfig, "", " ")
if err != nil {
h.Log.Warnf("Failed to create jsconfig.json: %s", err)

} else {
filename := filepath.Join(assetsDir, "jsconfig.json")
if h.running {
h.skipRebuildForFilenamesMu.Lock()
h.skipRebuildForFilenames[filename] = true
h.skipRebuildForFilenamesMu.Unlock()
}
// Make sure it's written to the OS fs as this is used by
// editors.
if err := afero.WriteFile(hugofs.Os, filename, b, 0666); err != nil {
h.Log.Warnf("Failed to write jsconfig.json: %s", err)
} else {
filename := filepath.Join(assetsDir, "jsconfig.json")
if h.running {
h.skipRebuildForFilenamesMu.Lock()
h.skipRebuildForFilenames[filename] = true
h.skipRebuildForFilenamesMu.Unlock()
}
// Make sure it's written to the OS fs as this is used by
// editors.
if err := afero.WriteFile(hugofs.Os, filename, b, 0666); err != nil {
h.Log.Warnf("Failed to write jsconfig.json: %s", err)
}
}
}
}
Expand Down
17 changes: 14 additions & 3 deletions hugolib/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,21 +176,32 @@ path="github.com/gohugoio/hugoTestProjectJSModImports"
go 1.15
require github.com/gohugoio/hugoTestProjectJSModImports v0.3.0 // indirect
require github.com/gohugoio/hugoTestProjectJSModImports v0.5.0 // indirect
`)

b.WithContent("p1.md", "").WithNothingAdded()

b.WithSourceFile("package.json", `{
"dependencies": {
"date-fns": "^2.16.1"
}
}`)

b.Assert(os.Chdir(workDir), qt.IsNil)
_, err = exec.Command("npm", "install").CombinedOutput()
b.Assert(err, qt.IsNil)

b.Build(BuildCfg{})

b.AssertFileContent("public/js/main.js", `
greeting: "greeting configured in mod2"
Hello1 from mod1: $
return "Hello2 from mod1";
var Hugo = "Rocks!";
return "Hello3 from mod2";
return "Hello from lib in the main project";
Hello3 from mod2. Date from date-fns: ${today}
Hello from lib in the main project
Hello5 from mod2.
var myparam = "Hugo Rocks!";`)

}
80 changes: 51 additions & 29 deletions resources/resource_transformers/js/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"

Expand Down Expand Up @@ -79,9 +78,11 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
return err
}

sdir, _ := path.Split(ctx.SourcePath)
//sdir, _ := path.Split(ctx.SourcePath)
opts.sourcefile = ctx.SourcePath
opts.resolveDir = t.c.sfs.RealFilename(sdir)
opts.workDir = t.c.rs.WorkingDir
opts.resolveDir = opts.workDir // t.c.sfs.RealFilename(sdir)
//fmt.Println(opts.resolveDir, "vs", t.c.rs.WorkingDir)
opts.workDir = t.c.rs.WorkingDir
opts.contents = string(src)
opts.mediaType = ctx.InMediaType
Expand All @@ -99,39 +100,60 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
result := api.Build(buildOptions)

if len(result.Errors) > 0 {
first := result.Errors[0]
loc := first.Location
path := loc.File

var err error
var f afero.File
var filename string

if !strings.HasPrefix(path, "..") {
// Try first in the assets fs
var fi os.FileInfo
fi, err = t.c.rs.BaseFs.Assets.Fs.Stat(path)

createErr := func(msg api.Message) (error, bool) {
loc := msg.Location
path := loc.File

var err error
var f afero.File
var filename string

if !strings.HasPrefix(path, "..") {
// Try first in the assets fs
var fi os.FileInfo
fi, err = t.c.rs.BaseFs.Assets.Fs.Stat(path)
if err == nil {
m := fi.(hugofs.FileMetaInfo).Meta()
filename = m.Filename()
f, err = m.Open()
}
}

if f == nil {
path = filepath.Join(t.c.rs.WorkingDir, path)
filename = path
f, err = t.c.rs.Fs.Os.Open(path)
}

if err == nil {
m := fi.(hugofs.FileMetaInfo).Meta()
filename = m.Filename()
f, err = m.Open()
fe := herrors.NewFileError("js", 0, loc.Line, loc.Column, errors.New(msg.Text))
err, _ := herrors.WithFileContext(fe, filename, f, herrors.SimpleLineMatcher)
f.Close()
return err, true
}
}

if f == nil {
path = filepath.Join(t.c.rs.WorkingDir, path)
filename = path
f, err = t.c.rs.Fs.Os.Open(path)
return fmt.Errorf("%s", msg.Text), false
}

if err == nil {
fe := herrors.NewFileError("js", 0, loc.Line, loc.Column, errors.New(first.Text))
err, _ := herrors.WithFileContext(fe, filename, f, herrors.SimpleLineMatcher)
f.Close()
return err
var errors []error
var theErr error

for _, msg := range result.Errors {
err, isFileErr := createErr(msg)
if theErr == nil || isFileErr {
theErr = err
}
errors = append(errors, err)
}
// Return 1, log the rest.
for _, err := range errors {
if err != theErr {
t.c.rs.Logger.Errorf("js.Build failed: %s", err)
}
}

return fmt.Errorf("%s", result.Errors[0].Text)
return theErr
}

ctx.To.Write(result.OutputFiles[0].Contents)
Expand Down
69 changes: 59 additions & 10 deletions resources/resource_transformers/js/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package js
import (
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"sync"
Expand Down Expand Up @@ -111,28 +112,56 @@ type importCache struct {
m map[string]api.OnResolveResult
}

var extensionToLoaderMap = map[string]api.Loader{
".js": api.LoaderJS,
".mjs": api.LoaderJS,
".cjs": api.LoaderJS,
".jsx": api.LoaderJSX,
".ts": api.LoaderTS,
".tsx": api.LoaderTSX,
".css": api.LoaderCSS,
".json": api.LoaderJSON,
".txt": api.LoaderText,
}

func loaderFromFilename(filename string) api.Loader {
l, found := extensionToLoaderMap[filepath.Ext(filename)]
if found {
return l
}
return api.LoaderJS
}

func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
fs := c.rs.Assets

const (
nsImportHugo = "ns-hugo"
nsParams = "ns-params"

stdinImporter = "<stdin>"
)

cache := importCache{
m: make(map[string]api.OnResolveResult),
}

resolveImport := func(args api.OnResolveArgs) (api.OnResolveResult, error) {
relDir := fs.MakePathRelative(args.ResolveDir)

if relDir == "" {
// Not in a Hugo Module, probably in node_modules.
return api.OnResolveResult{}, nil
isStdin := args.Importer == stdinImporter
var relDir string
if !isStdin {
relDir = filepath.Dir(fs.MakePathRelative(args.Importer))
} else {
relDir = filepath.Dir(opts.sourcefile)
}

impPath := args.Path

// stdin is the main entry file which already is at the relative root.
// Imports not starting with a "." is assumed to live relative to /assets.
// Hugo makes no assumptions about the directory structure below /assets.
if args.Importer != "<stdin>" && strings.HasPrefix(impPath, ".") {
impPath = filepath.Join(relDir, args.Path)
if relDir != "" && strings.HasPrefix(impPath, ".") {
impPath = filepath.Join(relDir, impPath)
}

findFirst := func(base string) hugofs.FileMeta {
Expand Down Expand Up @@ -164,6 +193,7 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
// It may be a regular file imported without an extension.
m = findFirst(impPath)
}
//

if m != nil {
// Store the source root so we can create a jsconfig.json
Expand All @@ -172,9 +202,11 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
// in server mode, we may get stale entries on renames etc.,
// but that shouldn't matter too much.
c.rs.JSConfigBuilder.AddSourceRoot(m.SourceRoot())
return api.OnResolveResult{Path: m.Filename(), Namespace: ""}, nil
return api.OnResolveResult{Path: m.Filename(), Namespace: nsImportHugo}, nil
}

// Not found in /assets. Probably in node_modules. ESBuild will handle that
// rather complex logic.
return api.OnResolveResult{}, nil
}

Expand Down Expand Up @@ -205,6 +237,23 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
return imp, nil

})
build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsImportHugo},
func(args api.OnLoadArgs) (api.OnLoadResult, error) {
b, err := ioutil.ReadFile(args.Path)

if err != nil {
return api.OnLoadResult{}, errors.Wrapf(err, "failed to read %q", args.Path)
}
c := string(b)
return api.OnLoadResult{
// See https://github.com/evanw/esbuild/issues/502
// This allows all modules to resolve dependencies
// in the main project's node_modules.
ResolveDir: opts.resolveDir,
Contents: &c,
Loader: loaderFromFilename(args.Path),
}, nil
})
},
}

Expand All @@ -226,10 +275,10 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
func(args api.OnResolveArgs) (api.OnResolveResult, error) {
return api.OnResolveResult{
Path: args.Path,
Namespace: "params",
Namespace: nsParams,
}, nil
})
build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: "params"},
build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsParams},
func(args api.OnLoadArgs) (api.OnLoadResult, error) {
return api.OnLoadResult{
Contents: &bs,
Expand Down

0 comments on commit d50379c

Please sign in to comment.