mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
Add "hugo mod npm pack"
This commit also introduces a convention where these common JS config files, including `package.hugo.json`, gets mounted into: ``` assets/_jsconfig ´`` These files mapped to their real filename will be added to the environment when running PostCSS, Babel etc., so you can do `process.env.HUGO_FILE_TAILWIND_CONFIG_JS` to resolve the real filename. But do note that `assets` is a composite/union filesystem, so if your config file is not meant to be overridden, name them something specific. This commit also adds adds `workDir/node_modules` to `NODE_PATH` and `HUGO_WORKDIR` to the env when running the JS tools above. Fixes #7644 Fixes #7656 Fixes #7675
This commit is contained in:
parent
9df60b62f9
commit
85ba9bfffb
16 changed files with 721 additions and 46 deletions
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 The Hugo Authors. All rights reserved.
|
// Copyright 2020 The Hugo Authors. All rights reserved.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -20,6 +20,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugolib"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/modules"
|
"github.com/gohugoio/hugo/modules"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
@ -114,6 +116,8 @@ This is not needed if you only operate on modules inside /themes or if you have
|
||||||
RunE: nil,
|
RunE: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(newModNPMCmd(c))
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
&cobra.Command{
|
&cobra.Command{
|
||||||
Use: "get",
|
Use: "get",
|
||||||
|
@ -272,6 +276,15 @@ func (c *modCmd) withModsClient(failOnMissingConfig bool, f func(*modules.Client
|
||||||
return f(com.hugo().ModulesClient)
|
return f(com.hugo().ModulesClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *modCmd) withHugo(f func(*hugolib.HugoSites) error) error {
|
||||||
|
com, err := c.initConfig(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return f(com.hugo())
|
||||||
|
}
|
||||||
|
|
||||||
func (c *modCmd) initConfig(failOnNoConfig bool) (*commandeer, error) {
|
func (c *modCmd) initConfig(failOnNoConfig bool) (*commandeer, error) {
|
||||||
com, err := initializeConfig(failOnNoConfig, false, &c.hugoBuilderCommon, c, nil)
|
com, err := initializeConfig(failOnNoConfig, false, &c.hugoBuilderCommon, c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
58
commands/mod_npm.go
Normal file
58
commands/mod_npm.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2020 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gohugoio/hugo/hugolib"
|
||||||
|
"github.com/gohugoio/hugo/modules/npm"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newModNPMCmd(c *modCmd) *cobra.Command {
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "npm",
|
||||||
|
Short: "Various npm helpers.",
|
||||||
|
Long: `Various npm (Node package manager) helpers.`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return c.withHugo(func(h *hugolib.HugoSites) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(&cobra.Command{
|
||||||
|
Use: "pack",
|
||||||
|
Short: "Experimental: Prepares and writes a composite package.json file for your project.",
|
||||||
|
Long: `Prepares and writes a composite package.json file for your project.
|
||||||
|
|
||||||
|
On first run it creates a "package.hugo.json" in the project root if not alread there. This file will be used as a template file
|
||||||
|
with the base dependency set.
|
||||||
|
|
||||||
|
This set will be merged with all "package.hugo.json" files found in the dependency tree, picking the version closest to the project.
|
||||||
|
|
||||||
|
This command is marked as 'Experimental'. We think it's a great idea, so it's not likely to be
|
||||||
|
removed from Hugo, but we need to test this out in "real life" to get a feel of it,
|
||||||
|
so this may/will change in future versions of Hugo.
|
||||||
|
`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
return c.withHugo(func(h *hugolib.HugoSites) error {
|
||||||
|
return npm.Pack(h.BaseFs.SourceFs, h.BaseFs.Assets.Dirs)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -17,8 +17,15 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugofs/files"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/config"
|
"github.com/gohugoio/hugo/config"
|
||||||
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -73,8 +80,23 @@ func NewInfo(environment string) Info {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetExecEnviron(cfg config.Provider) []string {
|
func GetExecEnviron(workDir string, cfg config.Provider, fs afero.Fs) []string {
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
|
nodepath := filepath.Join(workDir, "node_modules")
|
||||||
|
if np := os.Getenv("NODE_PATH"); np != "" {
|
||||||
|
nodepath = workDir + string(os.PathListSeparator) + np
|
||||||
|
}
|
||||||
|
config.SetEnvVars(&env, "NODE_PATH", nodepath)
|
||||||
|
config.SetEnvVars(&env, "HUGO_WORKDIR", workDir)
|
||||||
config.SetEnvVars(&env, "HUGO_ENVIRONMENT", cfg.GetString("environment"))
|
config.SetEnvVars(&env, "HUGO_ENVIRONMENT", cfg.GetString("environment"))
|
||||||
|
fis, err := afero.ReadDir(fs, files.FolderJSConfig)
|
||||||
|
if err == nil {
|
||||||
|
for _, fi := range fis {
|
||||||
|
key := fmt.Sprintf("HUGO_FILE_%s", strings.ReplaceAll(strings.ToUpper(fi.Name()), ".", "_"))
|
||||||
|
value := fi.(hugofs.FileMetaInfo).Meta().Filename()
|
||||||
|
config.SetEnvVars(&env, key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,30 @@ Hugo Pipe's Babel requires the `@babel/cli` and `@babel/core` JavaScript package
|
||||||
If you are using the Hugo Snap package, Babel and plugin(s) need to be installed locally within your Hugo site directory, e.g., `npm install @babel/cli @babel/core --save-dev` without the `-g` flag.
|
If you are using the Hugo Snap package, Babel and plugin(s) need to be installed locally within your Hugo site directory, e.g., `npm install @babel/cli @babel/core --save-dev` without the `-g` flag.
|
||||||
{{% /note %}}
|
{{% /note %}}
|
||||||
|
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
{{< new-in "v0.75.0" >}}
|
||||||
|
|
||||||
|
In Hugo `v0.75` we improved the way we resolve JS configuration and dependencies. One of them is that we now adds the main project's `node_modules` to `NODE_PATH` when running Babel and similar tools. There are some known [issues](https://github.com/babel/babel/issues/5618) with Babel in this area, so if you have a `babel.config.js` living in a Hugo Module (and not in the project itself), we recommend using `require` to load the presets/plugins, e.g.:
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
require('@babel/preset-env'),
|
||||||
|
{
|
||||||
|
useBuiltIns: 'entry',
|
||||||
|
corejs: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
config [string]
|
config [string]
|
||||||
|
|
|
@ -26,6 +26,13 @@ import (
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The NPM package.json "template" file.
|
||||||
|
FilenamePackageHugoJSON = "package.hugo.json"
|
||||||
|
// The NPM package file.
|
||||||
|
FilenamePackageJSON = "package.json"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// This should be the only list of valid extensions for content files.
|
// This should be the only list of valid extensions for content files.
|
||||||
contentFileExtensions = []string{
|
contentFileExtensions = []string{
|
||||||
|
@ -163,9 +170,12 @@ const (
|
||||||
ComponentFolderI18n = "i18n"
|
ComponentFolderI18n = "i18n"
|
||||||
|
|
||||||
FolderResources = "resources"
|
FolderResources = "resources"
|
||||||
|
FolderJSConfig = "_jsconfig" // Mounted below /assets with postcss.config.js etc.
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
JsConfigFolderMountPrefix = filepath.Join(ComponentFolderAssets, FolderJSConfig)
|
||||||
|
|
||||||
ComponentFolders = []string{
|
ComponentFolders = []string{
|
||||||
ComponentFolderArchetypes,
|
ComponentFolderArchetypes,
|
||||||
ComponentFolderStatic,
|
ComponentFolderStatic,
|
||||||
|
|
|
@ -42,9 +42,6 @@ func NewRootMappingFs(fs afero.Fs, rms ...RootMapping) (*RootMappingFs, error) {
|
||||||
(&rm).clean()
|
(&rm).clean()
|
||||||
|
|
||||||
fromBase := files.ResolveComponentFolder(rm.From)
|
fromBase := files.ResolveComponentFolder(rm.From)
|
||||||
if fromBase == "" {
|
|
||||||
panic("unrecognised component folder in" + rm.From)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rm.To) < 2 {
|
if len(rm.To) < 2 {
|
||||||
panic(fmt.Sprintf("invalid root mapping; from/to: %s/%s", rm.From, rm.To))
|
panic(fmt.Sprintf("invalid root mapping; from/to: %s/%s", rm.From, rm.To))
|
||||||
|
|
|
@ -21,8 +21,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/hugo"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/htesting"
|
"github.com/gohugoio/hugo/htesting"
|
||||||
|
@ -129,12 +127,6 @@ func TestWalkSymbolicLink(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("BasePath Fs", func(t *testing.T) {
|
t.Run("BasePath Fs", func(t *testing.T) {
|
||||||
if hugo.GoMinorVersion() < 12 {
|
|
||||||
// https://github.com/golang/go/issues/30520
|
|
||||||
// This is fixed in Go 1.13 and in the latest Go 1.12
|
|
||||||
t.Skip("skip this for Go <= 1.11 due to a bug in Go's stdlib")
|
|
||||||
|
|
||||||
}
|
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
docsFs := afero.NewBasePathFs(fs, docsDir)
|
docsFs := afero.NewBasePathFs(fs, docsDir)
|
||||||
|
|
|
@ -49,6 +49,9 @@ type BaseFs struct {
|
||||||
// SourceFilesystems contains the different source file systems.
|
// SourceFilesystems contains the different source file systems.
|
||||||
*SourceFilesystems
|
*SourceFilesystems
|
||||||
|
|
||||||
|
// The project source.
|
||||||
|
SourceFs afero.Fs
|
||||||
|
|
||||||
// The filesystem used to publish the rendered site.
|
// The filesystem used to publish the rendered site.
|
||||||
// This usually maps to /my-project/public.
|
// This usually maps to /my-project/public.
|
||||||
PublishFs afero.Fs
|
PublishFs afero.Fs
|
||||||
|
@ -100,6 +103,23 @@ func (b *BaseFs) RelContentDir(filename string) string {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveJSConfigFile resolves the JS-related config file to a absolute
|
||||||
|
// filename. One example of such would be postcss.config.js.
|
||||||
|
func (fs *BaseFs) ResolveJSConfigFile(name string) string {
|
||||||
|
// First look in assets/_jsconfig
|
||||||
|
fi, err := fs.Assets.Fs.Stat(filepath.Join(files.FolderJSConfig, name))
|
||||||
|
if err == nil {
|
||||||
|
return fi.(hugofs.FileMetaInfo).Meta().Filename()
|
||||||
|
}
|
||||||
|
// Fall back to the work dir.
|
||||||
|
fi, err = fs.Work.Stat(name)
|
||||||
|
if err == nil {
|
||||||
|
return fi.(hugofs.FileMetaInfo).Meta().Filename()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// SourceFilesystems contains the different source file systems. These can be
|
// SourceFilesystems contains the different source file systems. These can be
|
||||||
// composite file systems (theme and project etc.), and they have all root
|
// composite file systems (theme and project etc.), and they have all root
|
||||||
// set to the source type the provides: data, i18n, static, layouts.
|
// set to the source type the provides: data, i18n, static, layouts.
|
||||||
|
@ -346,8 +366,10 @@ func NewBase(p *paths.Paths, logger *loggers.Logger, options ...func(*BaseFs) er
|
||||||
}
|
}
|
||||||
|
|
||||||
publishFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Destination, p.AbsPublishDir))
|
publishFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Destination, p.AbsPublishDir))
|
||||||
|
sourceFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Source, p.WorkingDir))
|
||||||
|
|
||||||
b := &BaseFs{
|
b := &BaseFs{
|
||||||
|
SourceFs: sourceFs,
|
||||||
PublishFs: publishFs,
|
PublishFs: publishFs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,11 +718,16 @@ type filesystemsCollector struct {
|
||||||
|
|
||||||
func (c *filesystemsCollector) addDirs(rfs *hugofs.RootMappingFs) {
|
func (c *filesystemsCollector) addDirs(rfs *hugofs.RootMappingFs) {
|
||||||
for _, componentFolder := range files.ComponentFolders {
|
for _, componentFolder := range files.ComponentFolders {
|
||||||
dirs, err := rfs.Dirs(componentFolder)
|
c.addDir(rfs, componentFolder)
|
||||||
|
}
|
||||||
|
|
||||||
if err == nil {
|
}
|
||||||
c.overlayDirs[componentFolder] = append(c.overlayDirs[componentFolder], dirs...)
|
|
||||||
}
|
func (c *filesystemsCollector) addDir(rfs *hugofs.RootMappingFs, componentFolder string) {
|
||||||
|
dirs, err := rfs.Dirs(componentFolder)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
c.overlayDirs[componentFolder] = append(c.overlayDirs[componentFolder], dirs...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/modules/npm"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/loggers"
|
"github.com/gohugoio/hugo/common/loggers"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
@ -38,7 +40,6 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://github.com/gohugoio/hugo/issues/6730
|
|
||||||
func TestHugoModulesVariants(t *testing.T) {
|
func TestHugoModulesVariants(t *testing.T) {
|
||||||
if !isCI() {
|
if !isCI() {
|
||||||
t.Skip("skip (relative) long running modules test when running locally")
|
t.Skip("skip (relative) long running modules test when running locally")
|
||||||
|
@ -60,8 +61,10 @@ path="github.com/gohugoio/hugoTestModule2"
|
||||||
|
|
||||||
newTestBuilder := func(t testing.TB, moduleOpts string) (*sitesBuilder, func()) {
|
newTestBuilder := func(t testing.TB, moduleOpts string) (*sitesBuilder, func()) {
|
||||||
b := newTestSitesBuilder(t)
|
b := newTestSitesBuilder(t)
|
||||||
workingDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-modules-variants")
|
tempDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-modules-variants")
|
||||||
b.Assert(err, qt.IsNil)
|
b.Assert(err, qt.IsNil)
|
||||||
|
workingDir := filepath.Join(tempDir, "myhugosite")
|
||||||
|
b.Assert(os.MkdirAll(workingDir, 0777), qt.IsNil)
|
||||||
b.Fs = hugofs.NewDefault(viper.New())
|
b.Fs = hugofs.NewDefault(viper.New())
|
||||||
b.WithWorkingDir(workingDir).WithConfigFile("toml", createConfig(workingDir, moduleOpts))
|
b.WithWorkingDir(workingDir).WithConfigFile("toml", createConfig(workingDir, moduleOpts))
|
||||||
b.WithTemplates(
|
b.WithTemplates(
|
||||||
|
@ -129,6 +132,158 @@ JS imported in module: |
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Create package.json", func(t *testing.T) {
|
||||||
|
|
||||||
|
b, clean := newTestBuilder(t, "")
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
b.WithSourceFile("package.json", `{
|
||||||
|
"name": "mypack",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"nonon": "error"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
b.WithSourceFile("package.hugo.json", `{
|
||||||
|
"name": "mypack",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"foo": "1.2.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"postcss-cli": "7.8.0",
|
||||||
|
"tailwindcss": "1.8.0"
|
||||||
|
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
b.Assert(npm.Pack(b.H.BaseFs.SourceFs, b.H.BaseFs.Assets.Dirs), qt.IsNil)
|
||||||
|
|
||||||
|
b.AssertFileContentFn("package.json", func(s string) bool {
|
||||||
|
return s == `{
|
||||||
|
"comments": {
|
||||||
|
"dependencies": {
|
||||||
|
"foo": "project",
|
||||||
|
"react-dom": "github.com/gohugoio/hugoTestModule2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"@babel/core": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"@babel/preset-env": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"postcss-cli": "project",
|
||||||
|
"tailwindcss": "project"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"foo": "1.2.3",
|
||||||
|
"react-dom": "^16.13.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"@babel/preset-env": "7.9.5",
|
||||||
|
"postcss-cli": "7.8.0",
|
||||||
|
"tailwindcss": "1.8.0"
|
||||||
|
},
|
||||||
|
"name": "mypack",
|
||||||
|
"scripts": {},
|
||||||
|
"version": "1.2.3"
|
||||||
|
}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Create package.json, no default", func(t *testing.T) {
|
||||||
|
|
||||||
|
b, clean := newTestBuilder(t, "")
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
b.WithSourceFile("package.json", `{
|
||||||
|
"name": "mypack",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"moo": "1.2.3"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
b.Assert(npm.Pack(b.H.BaseFs.SourceFs, b.H.BaseFs.Assets.Dirs), qt.IsNil)
|
||||||
|
|
||||||
|
b.AssertFileContentFn("package.json", func(s string) bool {
|
||||||
|
return s == `{
|
||||||
|
"comments": {
|
||||||
|
"dependencies": {
|
||||||
|
"moo": "project",
|
||||||
|
"react-dom": "github.com/gohugoio/hugoTestModule2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"@babel/core": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"@babel/preset-env": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"postcss-cli": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"tailwindcss": "github.com/gohugoio/hugoTestModule2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"moo": "1.2.3",
|
||||||
|
"react-dom": "^16.13.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"@babel/preset-env": "7.9.5",
|
||||||
|
"postcss-cli": "7.1.0",
|
||||||
|
"tailwindcss": "1.2.0"
|
||||||
|
},
|
||||||
|
"name": "mypack",
|
||||||
|
"scripts": {},
|
||||||
|
"version": "1.2.3"
|
||||||
|
}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Create package.json, no default, no package.json", func(t *testing.T) {
|
||||||
|
|
||||||
|
b, clean := newTestBuilder(t, "")
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
b.Assert(npm.Pack(b.H.BaseFs.SourceFs, b.H.BaseFs.Assets.Dirs), qt.IsNil)
|
||||||
|
|
||||||
|
b.AssertFileContentFn("package.json", func(s string) bool {
|
||||||
|
return s == `{
|
||||||
|
"comments": {
|
||||||
|
"dependencies": {
|
||||||
|
"react-dom": "github.com/gohugoio/hugoTestModule2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"@babel/core": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"@babel/preset-env": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"postcss-cli": "github.com/gohugoio/hugoTestModule2",
|
||||||
|
"tailwindcss": "github.com/gohugoio/hugoTestModule2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react-dom": "^16.13.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"@babel/preset-env": "7.9.5",
|
||||||
|
"postcss-cli": "7.1.0",
|
||||||
|
"tailwindcss": "1.2.0"
|
||||||
|
},
|
||||||
|
"name": "myhugosite",
|
||||||
|
"version": "0.1.0"
|
||||||
|
}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bep) this fails when testmodBuilder is also building ...
|
// TODO(bep) this fails when testmodBuilder is also building ...
|
||||||
|
|
|
@ -873,6 +873,10 @@ func TestResourceChainPostCSS(t *testing.T) {
|
||||||
|
|
||||||
postcssConfig := `
|
postcssConfig := `
|
||||||
console.error("Hugo Environment:", process.env.HUGO_ENVIRONMENT );
|
console.error("Hugo Environment:", process.env.HUGO_ENVIRONMENT );
|
||||||
|
// https://github.com/gohugoio/hugo/issues/7656
|
||||||
|
console.error("package.json:", process.env.HUGO_FILE_PACKAGE_JSON );
|
||||||
|
console.error("PostCSS Config File:", process.env.HUGO_FILE_POSTCSS_CONFIG_JS );
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -954,6 +958,8 @@ class-in-b {
|
||||||
|
|
||||||
// Make sure Node sees this.
|
// Make sure Node sees this.
|
||||||
b.Assert(logBuf.String(), qt.Contains, "Hugo Environment: production")
|
b.Assert(logBuf.String(), qt.Contains, "Hugo Environment: production")
|
||||||
|
b.Assert(logBuf.String(), qt.Contains, fmt.Sprintf("PostCSS Config File: %s/postcss.config.js", workDir))
|
||||||
|
b.Assert(logBuf.String(), qt.Contains, fmt.Sprintf("package.json: %s/package.json", workDir))
|
||||||
|
|
||||||
b.AssertFileContent("public/index.html", `
|
b.AssertFileContent("public/index.html", `
|
||||||
Styles RelPermalink: /css/styles.css
|
Styles RelPermalink: /css/styles.css
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -382,6 +383,11 @@ func (c *collector) applyMounts(moduleImport Import, mod *moduleAdapter) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mounts, err = c.mountCommonJSConfig(mod, mounts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
mod.mounts = mounts
|
mod.mounts = mounts
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -549,6 +555,43 @@ func (c *collector) loadModules() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches postcss.config.js etc.
|
||||||
|
var commonJSConfigs = regexp.MustCompile(`(babel|postcss|tailwind)\.config\.js`)
|
||||||
|
|
||||||
|
func (c *collector) mountCommonJSConfig(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {
|
||||||
|
for _, m := range mounts {
|
||||||
|
if strings.HasPrefix(m.Target, files.JsConfigFolderMountPrefix) {
|
||||||
|
// This follows the convention of the other component types (assets, content, etc.),
|
||||||
|
// if one or more is specificed by the user, we skip the defaults.
|
||||||
|
// These mounts were added to Hugo in 0.75.
|
||||||
|
return mounts, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount the common JS config files.
|
||||||
|
fis, err := afero.ReadDir(c.fs, owner.Dir())
|
||||||
|
if err != nil {
|
||||||
|
return mounts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range fis {
|
||||||
|
n := fi.Name()
|
||||||
|
|
||||||
|
should := n == files.FilenamePackageHugoJSON || n == files.FilenamePackageJSON
|
||||||
|
should = should || commonJSConfigs.MatchString(n)
|
||||||
|
|
||||||
|
if should {
|
||||||
|
mounts = append(mounts, Mount{
|
||||||
|
Source: n,
|
||||||
|
Target: filepath.Join(files.ComponentFolderAssets, files.FolderJSConfig, n),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return mounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {
|
func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {
|
||||||
var out []Mount
|
var out []Mount
|
||||||
dir := owner.Dir()
|
dir := owner.Dir()
|
||||||
|
|
|
@ -56,7 +56,9 @@ func ApplyProjectConfigDefaults(cfg config.Provider, mod Module) error {
|
||||||
// the basic level.
|
// the basic level.
|
||||||
componentsConfigured := make(map[string]bool)
|
componentsConfigured := make(map[string]bool)
|
||||||
for _, mnt := range moda.mounts {
|
for _, mnt := range moda.mounts {
|
||||||
componentsConfigured[mnt.Component()] = true
|
if !strings.HasPrefix(mnt.Target, files.JsConfigFolderMountPrefix) {
|
||||||
|
componentsConfigured[mnt.Component()] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type dirKeyComponent struct {
|
type dirKeyComponent struct {
|
||||||
|
@ -318,12 +320,21 @@ type Mount struct {
|
||||||
Target string // relative target path, e.g. "assets/bootstrap/scss"
|
Target string // relative target path, e.g. "assets/bootstrap/scss"
|
||||||
|
|
||||||
Lang string // any language code associated with this mount.
|
Lang string // any language code associated with this mount.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Mount) Component() string {
|
func (m Mount) Component() string {
|
||||||
return strings.Split(m.Target, fileSeparator)[0]
|
return strings.Split(m.Target, fileSeparator)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Mount) ComponentAndName() (string, string) {
|
||||||
|
k := strings.Index(m.Target, fileSeparator)
|
||||||
|
if k == -1 {
|
||||||
|
return m.Target, ""
|
||||||
|
}
|
||||||
|
return m.Target[:k], m.Target[k+1:]
|
||||||
|
}
|
||||||
|
|
||||||
func getStaticDirs(cfg config.Provider) []string {
|
func getStaticDirs(cfg config.Provider) []string {
|
||||||
var staticDirs []string
|
var staticDirs []string
|
||||||
for i := -1; i <= 10; i++ {
|
for i := -1; i <= 10; i++ {
|
||||||
|
|
230
modules/npm/package_builder.go
Normal file
230
modules/npm/package_builder.go
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
// Copyright 2020 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package npm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugofs/files"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
|
"github.com/spf13/cast"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dependenciesKey = "dependencies"
|
||||||
|
devDependenciesKey = "devDependencies"
|
||||||
|
|
||||||
|
packageJSONName = "package.json"
|
||||||
|
|
||||||
|
packageJSONTemplate = `{
|
||||||
|
"name": "%s",
|
||||||
|
"version": "%s"
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func Pack(fs afero.Fs, fis []hugofs.FileMetaInfo) error {
|
||||||
|
|
||||||
|
var b *packageBuilder
|
||||||
|
|
||||||
|
// Have a package.hugo.json?
|
||||||
|
fi, err := fs.Stat(files.FilenamePackageHugoJSON)
|
||||||
|
if err != nil {
|
||||||
|
// Have a package.json?
|
||||||
|
fi, err = fs.Stat(packageJSONName)
|
||||||
|
if err != nil {
|
||||||
|
// Create one.
|
||||||
|
name := "project"
|
||||||
|
// Use the Hugo site's folder name as the default name.
|
||||||
|
// The owner can change it later.
|
||||||
|
rfi, err := fs.Stat("")
|
||||||
|
if err == nil {
|
||||||
|
name = rfi.Name()
|
||||||
|
}
|
||||||
|
packageJSONContent := fmt.Sprintf(packageJSONTemplate, name, "0.1.0")
|
||||||
|
if err = afero.WriteFile(fs, files.FilenamePackageHugoJSON, []byte(packageJSONContent), 0666); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fi, err = fs.Stat(files.FilenamePackageHugoJSON)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := fi.(hugofs.FileMetaInfo).Meta()
|
||||||
|
masterFilename := meta.Filename()
|
||||||
|
f, err := meta.Open()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "npm pack: failed to open package file")
|
||||||
|
}
|
||||||
|
b = newPackageBuilder(meta.Module(), f)
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
for _, fi := range fis {
|
||||||
|
if fi.IsDir() {
|
||||||
|
// We only care about the files in the root.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Name() != files.FilenamePackageHugoJSON {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := fi.(hugofs.FileMetaInfo).Meta()
|
||||||
|
|
||||||
|
if meta.Filename() == masterFilename {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := meta.Open()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "npm pack: failed to open package file")
|
||||||
|
}
|
||||||
|
b.Add(meta.Module(), f)
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Err() != nil {
|
||||||
|
return errors.Wrap(b.Err(), "npm pack: failed to build")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the dependencies in the original template with the merged set.
|
||||||
|
b.originalPackageJSON[dependenciesKey] = b.dependencies
|
||||||
|
b.originalPackageJSON[devDependenciesKey] = b.devDependencies
|
||||||
|
var commentsm map[string]interface{}
|
||||||
|
comments, found := b.originalPackageJSON["comments"]
|
||||||
|
if found {
|
||||||
|
commentsm = cast.ToStringMap(comments)
|
||||||
|
} else {
|
||||||
|
commentsm = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
commentsm[dependenciesKey] = b.dependenciesComments
|
||||||
|
commentsm[devDependenciesKey] = b.devDependenciesComments
|
||||||
|
b.originalPackageJSON["comments"] = commentsm
|
||||||
|
|
||||||
|
// Write it out to the project package.json
|
||||||
|
packageJSONData, err := json.MarshalIndent(b.originalPackageJSON, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "npm pack: failed to marshal JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := afero.WriteFile(fs, packageJSONName, packageJSONData, 0666); err != nil {
|
||||||
|
return errors.Wrap(err, "npm pack: failed to write package.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPackageBuilder(source string, first io.Reader) *packageBuilder {
|
||||||
|
b := &packageBuilder{
|
||||||
|
devDependencies: make(map[string]interface{}),
|
||||||
|
devDependenciesComments: make(map[string]interface{}),
|
||||||
|
dependencies: make(map[string]interface{}),
|
||||||
|
dependenciesComments: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
m := b.unmarshal(first)
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
b.addm(source, m)
|
||||||
|
b.originalPackageJSON = m
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
type packageBuilder struct {
|
||||||
|
err error
|
||||||
|
|
||||||
|
// The original package.hugo.json.
|
||||||
|
originalPackageJSON map[string]interface{}
|
||||||
|
|
||||||
|
devDependencies map[string]interface{}
|
||||||
|
devDependenciesComments map[string]interface{}
|
||||||
|
dependencies map[string]interface{}
|
||||||
|
dependenciesComments map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *packageBuilder) Add(source string, r io.Reader) *packageBuilder {
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
m := b.unmarshal(r)
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
b.addm(source, m)
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *packageBuilder) addm(source string, m map[string]interface{}) {
|
||||||
|
if source == "" {
|
||||||
|
source = "project"
|
||||||
|
}
|
||||||
|
|
||||||
|
// The version selection is currently very simple.
|
||||||
|
// We may consider minimal version selection or something
|
||||||
|
// after testing this out.
|
||||||
|
//
|
||||||
|
// But for now, the first version string for a given dependency wins.
|
||||||
|
// These packages will be added by order of import (project, module1, module2...),
|
||||||
|
// so that should at least give the project control over the situation.
|
||||||
|
if devDeps, found := m[devDependenciesKey]; found {
|
||||||
|
mm := cast.ToStringMapString(devDeps)
|
||||||
|
for k, v := range mm {
|
||||||
|
if _, added := b.devDependencies[k]; !added {
|
||||||
|
b.devDependencies[k] = v
|
||||||
|
b.devDependenciesComments[k] = source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if deps, found := m[dependenciesKey]; found {
|
||||||
|
mm := cast.ToStringMapString(deps)
|
||||||
|
for k, v := range mm {
|
||||||
|
if _, added := b.dependencies[k]; !added {
|
||||||
|
b.dependencies[k] = v
|
||||||
|
b.dependenciesComments[k] = source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *packageBuilder) unmarshal(r io.Reader) map[string]interface{} {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(helpers.ReaderToBytes(r), &m)
|
||||||
|
if err != nil {
|
||||||
|
b.err = err
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *packageBuilder) Err() error {
|
||||||
|
return b.err
|
||||||
|
}
|
95
modules/npm/package_builder_test.go
Normal file
95
modules/npm/package_builder_test.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright 2020 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package npm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
qt "github.com/frankban/quicktest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const templ = `{
|
||||||
|
"name": "foo",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"react-dom": "1.1.1",
|
||||||
|
"tailwindcss": "1.2.0",
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"@babel/preset-env": "7.9.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"postcss-cli": "7.1.0",
|
||||||
|
"tailwindcss": "1.2.0",
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"@babel/preset-env": "7.9.5"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestPackageBuilder(t *testing.T) {
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
b := newPackageBuilder("", strings.NewReader(templ))
|
||||||
|
c.Assert(b.Err(), qt.IsNil)
|
||||||
|
|
||||||
|
b.Add("mymod", strings.NewReader(`{
|
||||||
|
"dependencies": {
|
||||||
|
"react-dom": "9.1.1",
|
||||||
|
"add1": "1.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tailwindcss": "error",
|
||||||
|
"add2": "2.1.1"
|
||||||
|
}
|
||||||
|
}`))
|
||||||
|
|
||||||
|
b.Add("mymod", strings.NewReader(`{
|
||||||
|
"dependencies": {
|
||||||
|
"react-dom": "error",
|
||||||
|
"add1": "error",
|
||||||
|
"add3": "3.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tailwindcss": "error",
|
||||||
|
"add2": "error",
|
||||||
|
"add4": "4.1.1"
|
||||||
|
|
||||||
|
}
|
||||||
|
}`))
|
||||||
|
|
||||||
|
c.Assert(b.Err(), qt.IsNil)
|
||||||
|
|
||||||
|
c.Assert(b.dependencies, qt.DeepEquals, map[string]interface{}{
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"add1": "1.1.1",
|
||||||
|
"add3": "3.1.1",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"@babel/preset-env": "7.9.5",
|
||||||
|
"react-dom": "1.1.1",
|
||||||
|
"tailwindcss": "1.2.0",
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Assert(b.devDependencies, qt.DeepEquals, map[string]interface{}{
|
||||||
|
"tailwindcss": "1.2.0",
|
||||||
|
"@babel/cli": "7.8.4",
|
||||||
|
"@babel/core": "7.9.0",
|
||||||
|
"add2": "2.1.1",
|
||||||
|
"add4": "4.1.1",
|
||||||
|
"@babel/preset-env": "7.9.5",
|
||||||
|
"postcss-cli": "7.1.0",
|
||||||
|
})
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
package babel
|
package babel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -27,7 +28,6 @@ import (
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/herrors"
|
"github.com/gohugoio/hugo/common/herrors"
|
||||||
"github.com/gohugoio/hugo/hugofs"
|
|
||||||
"github.com/gohugoio/hugo/resources"
|
"github.com/gohugoio/hugo/resources"
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -120,6 +120,9 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
var configFile string
|
var configFile string
|
||||||
logger := t.rs.Logger
|
logger := t.rs.Logger
|
||||||
|
|
||||||
|
var errBuf bytes.Buffer
|
||||||
|
infoW := loggers.LoggerToWriterWithPrefix(logger.INFO, "babel")
|
||||||
|
|
||||||
if t.options.Config != "" {
|
if t.options.Config != "" {
|
||||||
configFile = t.options.Config
|
configFile = t.options.Config
|
||||||
} else {
|
} else {
|
||||||
|
@ -130,16 +133,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
|
|
||||||
// We need an abolute filename to the config file.
|
// We need an abolute filename to the config file.
|
||||||
if !filepath.IsAbs(configFile) {
|
if !filepath.IsAbs(configFile) {
|
||||||
// We resolve this against the virtual Work filesystem, to allow
|
configFile = t.rs.BaseFs.ResolveJSConfigFile(configFile)
|
||||||
// this config file to live in one of the themes if needed.
|
if configFile == "" && t.options.Config != "" {
|
||||||
fi, err := t.rs.BaseFs.Work.Stat(configFile)
|
// Only fail if the user specificed config file is not found.
|
||||||
if err != nil {
|
return errors.Errorf("babel config %q not found:", configFile)
|
||||||
if t.options.Config != "" {
|
|
||||||
// Only fail if the user specificed config file is not found.
|
|
||||||
return errors.Wrapf(err, "babel config %q not found:", configFile)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
configFile = fi.(hugofs.FileMetaInfo).Meta().Filename()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,8 +155,8 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
cmd := exec.Command(binary, cmdArgs...)
|
cmd := exec.Command(binary, cmdArgs...)
|
||||||
|
|
||||||
cmd.Stdout = ctx.To
|
cmd.Stdout = ctx.To
|
||||||
cmd.Stderr = loggers.LoggerToWriterWithPrefix(logger.INFO, "babel")
|
cmd.Stderr = io.MultiWriter(infoW, &errBuf)
|
||||||
cmd.Env = hugo.GetExecEnviron(t.rs.Cfg)
|
cmd.Env = hugo.GetExecEnviron(t.rs.WorkingDir, t.rs.Cfg, t.rs.BaseFs.Assets.Fs)
|
||||||
|
|
||||||
stdin, err := cmd.StdinPipe()
|
stdin, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -173,7 +170,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, errBuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -170,17 +170,11 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
|
||||||
|
|
||||||
// We need an abolute filename to the config file.
|
// We need an abolute filename to the config file.
|
||||||
if !filepath.IsAbs(configFile) {
|
if !filepath.IsAbs(configFile) {
|
||||||
// We resolve this against the virtual Work filesystem, to allow
|
configFile = t.rs.BaseFs.ResolveJSConfigFile(configFile)
|
||||||
// this config file to live in one of the themes if needed.
|
if configFile == "" && t.options.Config != "" {
|
||||||
fi, err := t.rs.BaseFs.Work.Stat(configFile)
|
// Only fail if the user specificed config file is not found.
|
||||||
if err != nil {
|
return errors.Errorf("postcss config %q not found:", configFile)
|
||||||
if t.options.Config != "" {
|
|
||||||
// Only fail if the user specificed config file is not found.
|
|
||||||
return errors.Wrapf(err, "postcss config %q not found:", configFile)
|
|
||||||
}
|
|
||||||
configFile = ""
|
|
||||||
} else {
|
|
||||||
configFile = fi.(hugofs.FileMetaInfo).Meta().Filename()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +196,8 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
|
||||||
|
|
||||||
cmd.Stdout = ctx.To
|
cmd.Stdout = ctx.To
|
||||||
cmd.Stderr = io.MultiWriter(infoW, &errBuf)
|
cmd.Stderr = io.MultiWriter(infoW, &errBuf)
|
||||||
cmd.Env = hugo.GetExecEnviron(t.rs.Cfg)
|
|
||||||
|
cmd.Env = hugo.GetExecEnviron(t.rs.WorkingDir, t.rs.Cfg, t.rs.BaseFs.Assets.Fs)
|
||||||
|
|
||||||
stdin, err := cmd.StdinPipe()
|
stdin, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue