mirror of
https://github.com/gohugoio/hugo.git
synced 2025-03-20 23:11:43 +00:00
js: Misc fixes
* Fix resolve of package.json deps in submodules * Fix directory logic for writing assets/jsconfig.json Fixes #7924 Fixes #7923
This commit is contained in:
parent
cf6131dc18
commit
bf2837a314
6 changed files with 144 additions and 65 deletions
2
go.mod
2
go.mod
|
@ -15,7 +15,7 @@ require (
|
||||||
github.com/bep/tmc v0.5.1
|
github.com/bep/tmc v0.5.1
|
||||||
github.com/disintegration/gift v1.2.1
|
github.com/disintegration/gift v1.2.1
|
||||||
github.com/dustin/go-humanize v1.0.0
|
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/fortytw2/leaktest v1.3.0
|
||||||
github.com/frankban/quicktest v1.11.1
|
github.com/frankban/quicktest v1.11.1
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -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.1/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
|
||||||
github.com/evanw/esbuild v0.8.2 h1:pwvPPsU8dqwBLdPwBmETdp1ccpefC1l+8RKZD1PafcA=
|
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.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/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 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q=
|
||||||
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
|
|
|
@ -354,26 +354,31 @@ func (h *HugoSites) postProcess() error {
|
||||||
// Write a jsconfig.json file to the project's /asset directory
|
// Write a jsconfig.json file to the project's /asset directory
|
||||||
// to help JS intellisense in VS Code etc.
|
// to help JS intellisense in VS Code etc.
|
||||||
if !h.ResourceSpec.BuildConfig.NoJSConfigInAssets && h.BaseFs.Assets.Dirs != nil {
|
if !h.ResourceSpec.BuildConfig.NoJSConfigInAssets && h.BaseFs.Assets.Dirs != nil {
|
||||||
m := h.BaseFs.Assets.Dirs[0].Meta()
|
fi, err := h.BaseFs.Assets.Fs.Stat("")
|
||||||
assetsDir := m.Filename()
|
if err != nil {
|
||||||
if strings.HasPrefix(assetsDir, h.ResourceSpec.WorkingDir) {
|
h.Log.Warnf("Failed to resolve jsconfig.json dir: %s", err)
|
||||||
if jsConfig := h.ResourceSpec.JSConfigBuilder.Build(assetsDir); jsConfig != nil {
|
} 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, "", " ")
|
b, err := json.MarshalIndent(jsConfig, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.Log.Warnf("Failed to create jsconfig.json: %s", err)
|
h.Log.Warnf("Failed to create jsconfig.json: %s", err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
filename := filepath.Join(assetsDir, "jsconfig.json")
|
filename := filepath.Join(assetsDir, "jsconfig.json")
|
||||||
if h.running {
|
if h.running {
|
||||||
h.skipRebuildForFilenamesMu.Lock()
|
h.skipRebuildForFilenamesMu.Lock()
|
||||||
h.skipRebuildForFilenames[filename] = true
|
h.skipRebuildForFilenames[filename] = true
|
||||||
h.skipRebuildForFilenamesMu.Unlock()
|
h.skipRebuildForFilenamesMu.Unlock()
|
||||||
}
|
}
|
||||||
// Make sure it's written to the OS fs as this is used by
|
// Make sure it's written to the OS fs as this is used by
|
||||||
// editors.
|
// editors.
|
||||||
if err := afero.WriteFile(hugofs.Os, filename, b, 0666); err != nil {
|
if err := afero.WriteFile(hugofs.Os, filename, b, 0666); err != nil {
|
||||||
h.Log.Warnf("Failed to write jsconfig.json: %s", err)
|
h.Log.Warnf("Failed to write jsconfig.json: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,12 +176,22 @@ path="github.com/gohugoio/hugoTestProjectJSModImports"
|
||||||
|
|
||||||
go 1.15
|
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.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.Build(BuildCfg{})
|
||||||
|
|
||||||
b.AssertFileContent("public/js/main.js", `
|
b.AssertFileContent("public/js/main.js", `
|
||||||
|
@ -189,8 +199,9 @@ greeting: "greeting configured in mod2"
|
||||||
Hello1 from mod1: $
|
Hello1 from mod1: $
|
||||||
return "Hello2 from mod1";
|
return "Hello2 from mod1";
|
||||||
var Hugo = "Rocks!";
|
var Hugo = "Rocks!";
|
||||||
return "Hello3 from mod2";
|
Hello3 from mod2. Date from date-fns: ${today}
|
||||||
return "Hello from lib in the main project";
|
Hello from lib in the main project
|
||||||
|
Hello5 from mod2.
|
||||||
var myparam = "Hugo Rocks!";`)
|
var myparam = "Hugo Rocks!";`)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/hugofs"
|
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/herrors"
|
"github.com/gohugoio/hugo/common/herrors"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/hugolib/filesystems"
|
"github.com/gohugoio/hugo/hugolib/filesystems"
|
||||||
|
@ -79,10 +77,9 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sdir, _ := path.Split(ctx.SourcePath)
|
|
||||||
opts.sourcefile = ctx.SourcePath
|
opts.sourcefile = ctx.SourcePath
|
||||||
opts.resolveDir = t.c.sfs.RealFilename(sdir)
|
|
||||||
opts.workDir = t.c.rs.WorkingDir
|
opts.workDir = t.c.rs.WorkingDir
|
||||||
|
opts.resolveDir = opts.workDir
|
||||||
opts.contents = string(src)
|
opts.contents = string(src)
|
||||||
opts.mediaType = ctx.InMediaType
|
opts.mediaType = ctx.InMediaType
|
||||||
|
|
||||||
|
@ -99,39 +96,54 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
result := api.Build(buildOptions)
|
result := api.Build(buildOptions)
|
||||||
|
|
||||||
if len(result.Errors) > 0 {
|
if len(result.Errors) > 0 {
|
||||||
first := result.Errors[0]
|
|
||||||
loc := first.Location
|
|
||||||
path := loc.File
|
|
||||||
|
|
||||||
var err error
|
createErr := func(msg api.Message) error {
|
||||||
var f afero.File
|
loc := msg.Location
|
||||||
var filename string
|
path := loc.File
|
||||||
|
|
||||||
|
var (
|
||||||
|
f afero.File
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if strings.HasPrefix(path, nsImportHugo) {
|
||||||
|
path = strings.TrimPrefix(path, nsImportHugo+":")
|
||||||
|
f, err = hugofs.Os.Open(path)
|
||||||
|
} else {
|
||||||
|
var fi os.FileInfo
|
||||||
|
fi, err = t.c.sfs.Fs.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
m := fi.(hugofs.FileMetaInfo).Meta()
|
||||||
|
path = m.Filename()
|
||||||
|
f, err = m.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
if err == nil {
|
||||||
m := fi.(hugofs.FileMetaInfo).Meta()
|
fe := herrors.NewFileError("js", 0, loc.Line, loc.Column, errors.New(msg.Text))
|
||||||
filename = m.Filename()
|
err, _ := herrors.WithFileContext(fe, path, f, herrors.SimpleLineMatcher)
|
||||||
f, err = m.Open()
|
f.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%s", msg.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors []error
|
||||||
|
|
||||||
|
for _, msg := range result.Errors {
|
||||||
|
errors = append(errors, createErr(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 1, log the rest.
|
||||||
|
for i, err := range errors {
|
||||||
|
if i > 0 {
|
||||||
|
t.c.rs.Logger.Errorf("js.Build failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if f == nil {
|
return errors[0]
|
||||||
path = filepath.Join(t.c.rs.WorkingDir, path)
|
|
||||||
filename = path
|
|
||||||
f, err = t.c.rs.Fs.Os.Open(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("%s", result.Errors[0].Text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.To.Write(result.OutputFiles[0].Contents)
|
ctx.To.Write(result.OutputFiles[0].Contents)
|
||||||
|
|
|
@ -16,6 +16,7 @@ package js
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -31,6 +32,13 @@ import (
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nsImportHugo = "ns-hugo"
|
||||||
|
nsParams = "ns-params"
|
||||||
|
|
||||||
|
stdinImporter = "<stdin>"
|
||||||
|
)
|
||||||
|
|
||||||
// Options esbuild configuration
|
// Options esbuild configuration
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// If not set, the source path will be used as the base target path.
|
// If not set, the source path will be used as the base target path.
|
||||||
|
@ -111,6 +119,26 @@ type importCache struct {
|
||||||
m map[string]api.OnResolveResult
|
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) {
|
func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
|
||||||
fs := c.rs.Assets
|
fs := c.rs.Assets
|
||||||
|
|
||||||
|
@ -119,20 +147,21 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveImport := func(args api.OnResolveArgs) (api.OnResolveResult, error) {
|
resolveImport := func(args api.OnResolveArgs) (api.OnResolveResult, error) {
|
||||||
relDir := fs.MakePathRelative(args.ResolveDir)
|
|
||||||
|
|
||||||
if relDir == "" {
|
isStdin := args.Importer == stdinImporter
|
||||||
// Not in a Hugo Module, probably in node_modules.
|
var relDir string
|
||||||
return api.OnResolveResult{}, nil
|
if !isStdin {
|
||||||
|
relDir = filepath.Dir(fs.MakePathRelative(args.Importer))
|
||||||
|
} else {
|
||||||
|
relDir = filepath.Dir(opts.sourcefile)
|
||||||
}
|
}
|
||||||
|
|
||||||
impPath := args.Path
|
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.
|
// Imports not starting with a "." is assumed to live relative to /assets.
|
||||||
// Hugo makes no assumptions about the directory structure below /assets.
|
// Hugo makes no assumptions about the directory structure below /assets.
|
||||||
if args.Importer != "<stdin>" && strings.HasPrefix(impPath, ".") {
|
if relDir != "" && strings.HasPrefix(impPath, ".") {
|
||||||
impPath = filepath.Join(relDir, args.Path)
|
impPath = filepath.Join(relDir, impPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
findFirst := func(base string) hugofs.FileMeta {
|
findFirst := func(base string) hugofs.FileMeta {
|
||||||
|
@ -164,6 +193,7 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
|
||||||
// It may be a regular file imported without an extension.
|
// It may be a regular file imported without an extension.
|
||||||
m = findFirst(impPath)
|
m = findFirst(impPath)
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
|
||||||
if m != nil {
|
if m != nil {
|
||||||
// Store the source root so we can create a jsconfig.json
|
// Store the source root so we can create a jsconfig.json
|
||||||
|
@ -172,9 +202,11 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
|
||||||
// in server mode, we may get stale entries on renames etc.,
|
// in server mode, we may get stale entries on renames etc.,
|
||||||
// but that shouldn't matter too much.
|
// but that shouldn't matter too much.
|
||||||
c.rs.JSConfigBuilder.AddSourceRoot(m.SourceRoot())
|
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
|
return api.OnResolveResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,6 +237,23 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
|
||||||
return imp, nil
|
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
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,10 +275,10 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
|
||||||
func(args api.OnResolveArgs) (api.OnResolveResult, error) {
|
func(args api.OnResolveArgs) (api.OnResolveResult, error) {
|
||||||
return api.OnResolveResult{
|
return api.OnResolveResult{
|
||||||
Path: args.Path,
|
Path: args.Path,
|
||||||
Namespace: "params",
|
Namespace: nsParams,
|
||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: "params"},
|
build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsParams},
|
||||||
func(args api.OnLoadArgs) (api.OnLoadResult, error) {
|
func(args api.OnLoadArgs) (api.OnLoadResult, error) {
|
||||||
return api.OnLoadResult{
|
return api.OnLoadResult{
|
||||||
Contents: &bs,
|
Contents: &bs,
|
||||||
|
|
Loading…
Reference in a new issue