2016-08-05 05:04:52 -04:00
// Copyright 2016-present 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 hugolib
import (
Add Hugo Modules
This commit implements Hugo Modules.
This is a broad subject, but some keywords include:
* A new `module` configuration section where you can import almost anything. You can configure both your own file mounts nd the file mounts of the modules you import. This is the new recommended way of configuring what you earlier put in `configDir`, `staticDir` etc. And it also allows you to mount folders in non-Hugo-projects, e.g. the `SCSS` folder in the Bootstrap GitHub project.
* A module consists of a set of mounts to the standard 7 component types in Hugo: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, and `archetypes`. Yes, Theme Components can now include content, which should be very useful, especially in bigger multilingual projects.
* Modules not in your local file cache will be downloaded automatically and even "hot replaced" while the server is running.
* Hugo Modules supports and encourages semver versioned modules, and uses the minimal version selection algorithm to resolve versions.
* A new set of CLI commands are provided to manage all of this: `hugo mod init`, `hugo mod get`, `hugo mod graph`, `hugo mod tidy`, and `hugo mod vendor`.
All of the above is backed by Go Modules.
Fixes #5973
Fixes #5996
Fixes #6010
Fixes #5911
Fixes #5940
Fixes #6074
Fixes #6082
Fixes #6092
2019-05-03 03:16:58 -04:00
"bytes"
"fmt"
"path/filepath"
2021-06-09 04:58:18 -04:00
"strings"
2016-08-05 05:04:52 -04:00
"testing"
2021-07-15 09:31:50 -04:00
"github.com/gohugoio/hugo/config"
2023-01-04 12:24:36 -05:00
"github.com/gohugoio/hugo/config/allconfig"
2021-06-09 04:58:18 -04:00
2019-08-10 15:05:17 -04:00
qt "github.com/frankban/quicktest"
2021-06-09 04:58:18 -04:00
"github.com/gohugoio/hugo/common/maps"
2017-02-04 22:20:06 -05:00
"github.com/spf13/afero"
2016-08-05 05:04:52 -04:00
)
2023-01-04 12:24:36 -05:00
func TestLoadConfigLanguageParamsOverrideIssue10620 ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.com"
disableKinds = [ "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" , "page" , "setion" ]
title = "Base Title"
staticDir = "mystatic"
[ params ]
[ params . comments ]
color = "blue"
title = "Default Comments Title"
[ languages ]
[ languages . en ]
title = "English Title"
[ languages . en . params . comments ]
title = "English Comments Title"
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
enSite := b . H . Sites [ 0 ]
b . Assert ( enSite . Title ( ) , qt . Equals , "English Title" )
b . Assert ( enSite . Home ( ) . Title ( ) , qt . Equals , "English Title" )
b . Assert ( enSite . Params ( ) , qt . DeepEquals , maps . Params {
"comments" : maps . Params {
"color" : "blue" ,
"title" : "English Comments Title" ,
} ,
} ,
)
}
2017-02-04 22:20:06 -05:00
func TestLoadConfig ( t * testing . T ) {
2023-01-04 12:24:36 -05:00
t . Run ( "2 languages" , func ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.com"
disableKinds = [ "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" , "page" , "setion" ]
title = "Base Title"
staticDir = "mystatic"
[ params ]
p1 = "p1base"
p2 = "p2base"
[ languages ]
[ languages . en ]
title = "English Title"
[ languages . en . params ]
myparam = "enParamValue"
p1 = "p1en"
weight = 1
[ languages . sv ]
title = "Svensk Title"
staticDir = "mysvstatic"
weight = 2
[ languages . sv . params ]
myparam = "svParamValue"
2018-03-18 06:07:24 -04:00
2021-07-15 09:31:50 -04:00
2023-01-04 12:24:36 -05:00
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
enSite := b . H . Sites [ 0 ]
svSite := b . H . Sites [ 1 ]
b . Assert ( enSite . Title ( ) , qt . Equals , "English Title" )
b . Assert ( enSite . Home ( ) . Title ( ) , qt . Equals , "English Title" )
b . Assert ( enSite . Params ( ) [ "myparam" ] , qt . Equals , "enParamValue" )
b . Assert ( enSite . Params ( ) [ "p1" ] , qt . Equals , "p1en" )
b . Assert ( enSite . Params ( ) [ "p2" ] , qt . Equals , "p2base" )
b . Assert ( svSite . Params ( ) [ "p1" ] , qt . Equals , "p1base" )
b . Assert ( enSite . conf . StaticDir [ 0 ] , qt . Equals , "mystatic" )
b . Assert ( svSite . Title ( ) , qt . Equals , "Svensk Title" )
b . Assert ( svSite . Home ( ) . Title ( ) , qt . Equals , "Svensk Title" )
b . Assert ( svSite . Params ( ) [ "myparam" ] , qt . Equals , "svParamValue" )
b . Assert ( svSite . conf . StaticDir [ 0 ] , qt . Equals , "mysvstatic" )
} )
t . Run ( "disable default language" , func ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.com"
disableKinds = [ "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" , "page" , "setion" ]
title = "Base Title"
defaultContentLanguage = "sv"
disableLanguages = [ "sv" ]
[ languages . en ]
weight = 1
[ languages . sv ]
weight = 2
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNotNil )
b . Assert ( err . Error ( ) , qt . Contains , "cannot disable default content language" )
} )
t . Run ( "no internal config from outside" , func ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.com"
[ internal ]
running = true
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . Assert ( b . H . Conf . Running ( ) , qt . Equals , false )
} )
t . Run ( "env overrides" , func ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.com"
disableKinds = [ "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" , "page" , "setion" ]
title = "Base Title"
[ params ]
p1 = "p1base"
p2 = "p2base"
[ params . pm2 ]
pm21 = "pm21base"
pm22 = "pm22base"
-- layouts / index . html --
p1 : { { . Site . Params . p1 } }
p2 : { { . Site . Params . p2 } }
pm21 : { { . Site . Params . pm2 . pm21 } }
pm22 : { { . Site . Params . pm2 . pm22 } }
pm31 : { { . Site . Params . pm3 . pm31 } }
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
Environ : [ ] string { "HUGO_PARAMS_P2=p2env" , "HUGO_PARAMS_PM2_PM21=pm21env" , "HUGO_PARAMS_PM3_PM31=pm31env" } ,
} ,
) . Build ( )
b . AssertFileContent ( "public/index.html" , "p1: p1base\np2: p2env\npm21: pm21env\npm22: pm22base\npm31: pm31env" )
2021-07-15 09:31:50 -04:00
} )
2023-01-04 12:24:36 -05:00
}
func TestLoadConfigThemeLanguage ( t * testing . T ) {
t . Parallel ( )
files := `
-- / hugo . toml --
baseURL = "https://example.com"
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
theme = "mytheme"
[ languages ]
[ languages . en ]
title = "English Title"
weight = 1
[ languages . sv ]
weight = 2
-- themes / mytheme / hugo . toml --
[ params ]
p1 = "p1base"
[ languages ]
[ languages . en ]
title = "English Title Theme"
[ languages . en . params ]
p2 = "p2en"
[ languages . en . params . sub ]
sub1 = "sub1en"
[ languages . sv ]
title = "Svensk Title Theme"
-- layouts / index . html --
title : { { . Title } } |
p1 : { { . Site . Params . p1 } } |
p2 : { { . Site . Params . p2 } } |
sub : { { . Site . Params . sub } } |
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . AssertFileContent ( "public/en/index.html" , `
title : English Title |
p1 : p1base
p2 : p2en
sub : map [ sub1 : sub1en ]
` )
2016-08-05 05:04:52 -04:00
}
2018-03-18 06:07:24 -04:00
2017-08-09 14:13:02 -04:00
func TestLoadMultiConfig ( t * testing . T ) {
t . Parallel ( )
2019-08-10 15:05:17 -04:00
c := qt . New ( t )
2018-03-18 06:07:24 -04:00
2017-08-09 14:13:02 -04:00
// Add a random config variable for testing.
// side = page in Norwegian.
configContentBase := `
2023-01-04 12:24:36 -05:00
Paginate = 32
2017-08-09 14:13:02 -04:00
PaginatePath = "side"
`
configContentSub := `
PaginatePath = "top"
`
mm := afero . NewMemMapFs ( )
writeToFs ( t , mm , "base.toml" , configContentBase )
writeToFs ( t , mm , "override.toml" , configContentSub )
2023-01-04 12:24:36 -05:00
all , err := allconfig . LoadConfig ( allconfig . ConfigSourceDescriptor { Fs : mm , Filename : "base.toml,override.toml" } )
2019-08-10 15:05:17 -04:00
c . Assert ( err , qt . IsNil )
2023-01-04 12:24:36 -05:00
cfg := all . Base
c . Assert ( cfg . PaginatePath , qt . Equals , "top" )
c . Assert ( cfg . Paginate , qt . Equals , 32 )
2017-08-09 14:13:02 -04:00
2018-03-18 06:07:24 -04:00
}
2021-06-22 12:17:49 -04:00
func TestLoadConfigFromThemes ( t * testing . T ) {
2018-03-18 06:07:24 -04:00
t . Parallel ( )
2019-08-10 15:05:17 -04:00
c := qt . New ( t )
2018-03-18 06:07:24 -04:00
2021-06-09 04:58:18 -04:00
mainConfigTemplate := `
2018-03-18 06:07:24 -04:00
theme = "test-theme"
baseURL = "https://example.com/"
[ frontmatter ]
date = [ "date" , "publishDate" ]
[ params ]
2021-06-09 04:58:18 -04:00
MERGE_PARAMS
2018-03-18 06:07:24 -04:00
p1 = "p1 main"
2021-06-09 04:58:18 -04:00
[ params . b ]
b1 = "b1 main"
[ params . b . c ]
bc1 = "bc1 main"
2018-03-18 06:07:24 -04:00
[ mediaTypes ]
[ mediaTypes . "text/m1" ]
2018-08-28 08:18:12 -04:00
suffixes = [ "m1main" ]
2018-03-18 06:07:24 -04:00
[ outputFormats . o1 ]
mediaType = "text/m1"
baseName = "o1main"
[ languages ]
[ languages . en ]
languageName = "English"
[ languages . en . params ]
pl1 = "p1-en-main"
[ languages . nb ]
languageName = "Norsk"
[ languages . nb . params ]
pl1 = "p1-nb-main"
[ [ menu . main ] ]
name = "menu-main-main"
[ [ menu . top ] ]
name = "menu-top-main"
`
themeConfig := `
baseURL = "http://bep.is/"
2021-08-22 05:57:37 -04:00
# Can not be set in theme .
disableKinds = [ "taxonomy" , "term" ]
2018-03-18 06:07:24 -04:00
# Can not be set in theme .
[ frontmatter ]
expiryDate = [ "date" ]
[ params ]
p1 = "p1 theme"
p2 = "p2 theme"
2021-06-09 04:58:18 -04:00
[ params . b ]
b1 = "b1 theme"
b2 = "b2 theme"
[ params . b . c ]
bc1 = "bc1 theme"
bc2 = "bc2 theme"
[ params . b . c . d ]
bcd1 = "bcd1 theme"
2018-03-18 06:07:24 -04:00
[ mediaTypes ]
[ mediaTypes . "text/m1" ]
2018-08-28 08:18:12 -04:00
suffixes = [ "m1theme" ]
2018-03-18 06:07:24 -04:00
[ mediaTypes . "text/m2" ]
2018-08-28 08:18:12 -04:00
suffixes = [ "m2theme" ]
2018-03-18 06:07:24 -04:00
[ outputFormats . o1 ]
mediaType = "text/m1"
baseName = "o1theme"
[ outputFormats . o2 ]
mediaType = "text/m2"
baseName = "o2theme"
[ languages ]
[ languages . en ]
languageName = "English2"
[ languages . en . params ]
pl1 = "p1-en-theme"
pl2 = "p2-en-theme"
[ [ languages . en . menu . main ] ]
name = "menu-lang-en-main"
[ [ languages . en . menu . theme ] ]
name = "menu-lang-en-theme"
[ languages . nb ]
languageName = "Norsk2"
[ languages . nb . params ]
pl1 = "p1-nb-theme"
pl2 = "p2-nb-theme"
top = "top-nb-theme"
[ [ languages . nb . menu . main ] ]
name = "menu-lang-nb-main"
[ [ languages . nb . menu . theme ] ]
name = "menu-lang-nb-theme"
[ [ languages . nb . menu . top ] ]
name = "menu-lang-nb-top"
[ [ menu . main ] ]
name = "menu-main-theme"
[ [ menu . thememenu ] ]
name = "menu-theme"
`
2021-07-03 04:40:59 -04:00
buildForConfig := func ( t testing . TB , mainConfig , themeConfig string ) * sitesBuilder {
2021-06-09 04:58:18 -04:00
b := newTestSitesBuilder ( t )
b . WithConfigFile ( "toml" , mainConfig ) . WithThemeConfigFile ( "toml" , themeConfig )
2021-06-22 12:17:49 -04:00
return b . Build ( BuildCfg { } )
}
buildForStrategy := func ( t testing . TB , s string ) * sitesBuilder {
mainConfig := strings . ReplaceAll ( mainConfigTemplate , "MERGE_PARAMS" , s )
2021-07-03 04:40:59 -04:00
return buildForConfig ( t , mainConfig , themeConfig )
2021-06-09 04:58:18 -04:00
}
2018-03-18 06:07:24 -04:00
2021-06-09 04:58:18 -04:00
c . Run ( "Merge default" , func ( c * qt . C ) {
b := buildForStrategy ( c , "" )
2023-01-04 12:24:36 -05:00
got := b . Configs . Base
2021-08-22 05:57:37 -04:00
2023-01-04 12:24:36 -05:00
b . Assert ( got . Params , qt . DeepEquals , maps . Params {
2021-06-09 04:58:18 -04:00
"b" : maps . Params {
"b1" : "b1 main" ,
"c" : maps . Params {
"bc1" : "bc1 main" ,
"bc2" : "bc2 theme" ,
"d" : maps . Params { "bcd1" : string ( "bcd1 theme" ) } ,
} ,
"b2" : "b2 theme" ,
} ,
"p2" : "p2 theme" ,
"p1" : "p1 main" ,
} )
2023-01-04 12:24:36 -05:00
c . Assert ( got . BaseURL , qt . Equals , "https://example.com/" )
2021-06-09 04:58:18 -04:00
} )
c . Run ( "Merge shallow" , func ( c * qt . C ) {
b := buildForStrategy ( c , fmt . Sprintf ( "_merge=%q" , "shallow" ) )
2023-01-04 12:24:36 -05:00
got := b . Configs . Base . Params
2021-06-09 04:58:18 -04:00
// Shallow merge, only add new keys to params.
2023-01-04 12:24:36 -05:00
b . Assert ( got , qt . DeepEquals , maps . Params {
2021-06-09 04:58:18 -04:00
"p1" : "p1 main" ,
"b" : maps . Params {
"b1" : "b1 main" ,
"c" : maps . Params {
"bc1" : "bc1 main" ,
} ,
} ,
"p2" : "p2 theme" ,
} )
} )
2018-03-18 06:07:24 -04:00
2021-06-22 12:17:49 -04:00
c . Run ( "Merge no params in project" , func ( c * qt . C ) {
b := buildForConfig (
2021-07-03 04:40:59 -04:00
c ,
2021-06-22 12:17:49 -04:00
"baseURL=\"https://example.org\"\ntheme = \"test-theme\"\n" ,
"[params]\np1 = \"p1 theme\"\n" ,
)
2023-01-04 12:24:36 -05:00
got := b . Configs . Base . Params
2021-06-22 12:17:49 -04:00
2023-01-04 12:24:36 -05:00
b . Assert ( got , qt . DeepEquals , maps . Params {
2021-06-22 12:17:49 -04:00
"p1" : "p1 theme" ,
} )
} )
2021-07-03 04:40:59 -04:00
// Issue #8724
for _ , mergeStrategy := range [ ] string { "none" , "shallow" } {
c . Run ( fmt . Sprintf ( "Merge with sitemap config in theme, mergestrategy %s" , mergeStrategy ) , func ( c * qt . C ) {
smapConfigTempl := ` [ sitemap ]
changefreq = % q
filename = "sitemap.xml"
priority = 0.5 `
b := buildForConfig (
c ,
fmt . Sprintf ( "_merge=%q\nbaseURL=\"https://example.org\"\ntheme = \"test-theme\"\n" , mergeStrategy ) ,
"baseURL=\"http://example.com\"\n" + fmt . Sprintf ( smapConfigTempl , "monthly" ) ,
)
2023-01-04 12:24:36 -05:00
got := b . Configs . Base
2021-07-03 04:40:59 -04:00
if mergeStrategy == "none" {
2023-01-04 12:24:36 -05:00
b . Assert ( got . Sitemap , qt . DeepEquals , config . SitemapConfig { ChangeFreq : "" , Priority : - 1 , Filename : "sitemap.xml" } )
2021-07-03 04:40:59 -04:00
b . AssertFileContent ( "public/sitemap.xml" , "schemas/sitemap" )
} else {
2023-01-04 12:24:36 -05:00
b . Assert ( got . Sitemap , qt . DeepEquals , config . SitemapConfig { ChangeFreq : "monthly" , Priority : - 1 , Filename : "sitemap.xml" } )
2021-07-03 04:40:59 -04:00
b . AssertFileContent ( "public/sitemap.xml" , "<changefreq>monthly</changefreq>" )
}
} )
}
2017-08-09 14:13:02 -04:00
}
2018-05-04 04:18:45 -04:00
2021-06-16 13:11:01 -04:00
func TestLoadConfigFromThemeDir ( t * testing . T ) {
t . Parallel ( )
mainConfig := `
theme = "test-theme"
[ params ]
m1 = "mv1"
`
themeConfig := `
[ params ]
t1 = "tv1"
t2 = "tv2"
`
themeConfigDir := filepath . Join ( "themes" , "test-theme" , "config" )
themeConfigDirDefault := filepath . Join ( themeConfigDir , "_default" )
themeConfigDirProduction := filepath . Join ( themeConfigDir , "production" )
projectConfigDir := "config"
b := newTestSitesBuilder ( t )
b . WithConfigFile ( "toml" , mainConfig ) . WithThemeConfigFile ( "toml" , themeConfig )
b . Assert ( b . Fs . Source . MkdirAll ( themeConfigDirDefault , 0777 ) , qt . IsNil )
b . Assert ( b . Fs . Source . MkdirAll ( themeConfigDirProduction , 0777 ) , qt . IsNil )
b . Assert ( b . Fs . Source . MkdirAll ( projectConfigDir , 0777 ) , qt . IsNil )
b . WithSourceFile ( filepath . Join ( projectConfigDir , "config.toml" ) , ` [ params ]
m2 = "mv2"
` )
b . WithSourceFile ( filepath . Join ( themeConfigDirDefault , "config.toml" ) , ` [ params ]
t2 = "tv2d"
t3 = "tv3d"
` )
b . WithSourceFile ( filepath . Join ( themeConfigDirProduction , "config.toml" ) , ` [ params ]
t3 = "tv3p"
` )
b . Build ( BuildCfg { } )
2023-01-04 12:24:36 -05:00
got := b . Configs . Base . Params
2021-06-16 13:11:01 -04:00
b . Assert ( got , qt . DeepEquals , maps . Params {
"t3" : "tv3p" ,
"m1" : "mv1" ,
"t1" : "tv1" ,
"t2" : "tv2d" ,
} )
}
2018-05-04 04:18:45 -04:00
func TestPrivacyConfig ( t * testing . T ) {
t . Parallel ( )
2019-08-10 15:05:17 -04:00
c := qt . New ( t )
2018-05-04 04:18:45 -04:00
tomlConfig := `
someOtherValue = "foo"
[ privacy ]
[ privacy . youtube ]
2018-05-21 08:02:30 -04:00
privacyEnhanced = true
2018-05-04 04:18:45 -04:00
`
b := newTestSitesBuilder ( t )
b . WithConfigFile ( "toml" , tomlConfig )
b . Build ( BuildCfg { SkipRender : true } )
2023-01-04 12:24:36 -05:00
c . Assert ( b . H . Sites [ 0 ] . Config ( ) . Privacy . YouTube . PrivacyEnhanced , qt . Equals , true )
2018-05-04 04:18:45 -04:00
}
Add Hugo Modules
This commit implements Hugo Modules.
This is a broad subject, but some keywords include:
* A new `module` configuration section where you can import almost anything. You can configure both your own file mounts nd the file mounts of the modules you import. This is the new recommended way of configuring what you earlier put in `configDir`, `staticDir` etc. And it also allows you to mount folders in non-Hugo-projects, e.g. the `SCSS` folder in the Bootstrap GitHub project.
* A module consists of a set of mounts to the standard 7 component types in Hugo: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, and `archetypes`. Yes, Theme Components can now include content, which should be very useful, especially in bigger multilingual projects.
* Modules not in your local file cache will be downloaded automatically and even "hot replaced" while the server is running.
* Hugo Modules supports and encourages semver versioned modules, and uses the minimal version selection algorithm to resolve versions.
* A new set of CLI commands are provided to manage all of this: `hugo mod init`, `hugo mod get`, `hugo mod graph`, `hugo mod tidy`, and `hugo mod vendor`.
All of the above is backed by Go Modules.
Fixes #5973
Fixes #5996
Fixes #6010
Fixes #5911
Fixes #5940
Fixes #6074
Fixes #6082
Fixes #6092
2019-05-03 03:16:58 -04:00
func TestLoadConfigModules ( t * testing . T ) {
t . Parallel ( )
2019-08-10 15:05:17 -04:00
c := qt . New ( t )
Add Hugo Modules
This commit implements Hugo Modules.
This is a broad subject, but some keywords include:
* A new `module` configuration section where you can import almost anything. You can configure both your own file mounts nd the file mounts of the modules you import. This is the new recommended way of configuring what you earlier put in `configDir`, `staticDir` etc. And it also allows you to mount folders in non-Hugo-projects, e.g. the `SCSS` folder in the Bootstrap GitHub project.
* A module consists of a set of mounts to the standard 7 component types in Hugo: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, and `archetypes`. Yes, Theme Components can now include content, which should be very useful, especially in bigger multilingual projects.
* Modules not in your local file cache will be downloaded automatically and even "hot replaced" while the server is running.
* Hugo Modules supports and encourages semver versioned modules, and uses the minimal version selection algorithm to resolve versions.
* A new set of CLI commands are provided to manage all of this: `hugo mod init`, `hugo mod get`, `hugo mod graph`, `hugo mod tidy`, and `hugo mod vendor`.
All of the above is backed by Go Modules.
Fixes #5973
Fixes #5996
Fixes #6010
Fixes #5911
Fixes #5940
Fixes #6074
Fixes #6082
Fixes #6092
2019-05-03 03:16:58 -04:00
// https://github.com/gohugoio/hugoThemes#themetoml
const (
// Before Hugo 0.56 each theme/component could have its own theme.toml
// with some settings, mostly used on the Hugo themes site.
// To preserve combability we read these files into the new "modules"
// section in config.toml.
o1t = `
name = "Component o1"
license = "MIT"
min_version = 0.38
`
// This is the component's config.toml, using the old theme syntax.
o1c = `
theme = [ "n2" ]
`
n1 = `
title = "Component n1"
[ module ]
description = "Component n1 description"
[ module . hugoVersion ]
min = "0.40.0"
max = "0.50.0"
extended = true
[ [ module . imports ] ]
path = "o1"
[ [ module . imports ] ]
path = "n3"
`
n2 = `
title = "Component n2"
`
n3 = `
title = "Component n3"
`
n4 = `
title = "Component n4"
`
)
b := newTestSitesBuilder ( t )
writeThemeFiles := func ( name , configTOML , themeTOML string ) {
b . WithSourceFile ( filepath . Join ( "themes" , name , "data" , "module.toml" ) , fmt . Sprintf ( "name=%q" , name ) )
if configTOML != "" {
b . WithSourceFile ( filepath . Join ( "themes" , name , "config.toml" ) , configTOML )
}
if themeTOML != "" {
b . WithSourceFile ( filepath . Join ( "themes" , name , "theme.toml" ) , themeTOML )
}
}
writeThemeFiles ( "n1" , n1 , "" )
writeThemeFiles ( "n2" , n2 , "" )
writeThemeFiles ( "n3" , n3 , "" )
writeThemeFiles ( "n4" , n4 , "" )
writeThemeFiles ( "o1" , o1c , o1t )
b . WithConfigFile ( "toml" , `
[ module ]
[ [ module . imports ] ]
path = "n1"
[ [ module . imports ] ]
path = "n4"
` )
b . Build ( BuildCfg { } )
2023-01-04 12:24:36 -05:00
modulesClient := b . H . Configs . ModulesClient
Add Hugo Modules
This commit implements Hugo Modules.
This is a broad subject, but some keywords include:
* A new `module` configuration section where you can import almost anything. You can configure both your own file mounts nd the file mounts of the modules you import. This is the new recommended way of configuring what you earlier put in `configDir`, `staticDir` etc. And it also allows you to mount folders in non-Hugo-projects, e.g. the `SCSS` folder in the Bootstrap GitHub project.
* A module consists of a set of mounts to the standard 7 component types in Hugo: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, and `archetypes`. Yes, Theme Components can now include content, which should be very useful, especially in bigger multilingual projects.
* Modules not in your local file cache will be downloaded automatically and even "hot replaced" while the server is running.
* Hugo Modules supports and encourages semver versioned modules, and uses the minimal version selection algorithm to resolve versions.
* A new set of CLI commands are provided to manage all of this: `hugo mod init`, `hugo mod get`, `hugo mod graph`, `hugo mod tidy`, and `hugo mod vendor`.
All of the above is backed by Go Modules.
Fixes #5973
Fixes #5996
Fixes #6010
Fixes #5911
Fixes #5940
Fixes #6074
Fixes #6082
Fixes #6092
2019-05-03 03:16:58 -04:00
var graphb bytes . Buffer
modulesClient . Graph ( & graphb )
2019-08-10 15:05:17 -04:00
expected := ` project n1
Add Hugo Modules
This commit implements Hugo Modules.
This is a broad subject, but some keywords include:
* A new `module` configuration section where you can import almost anything. You can configure both your own file mounts nd the file mounts of the modules you import. This is the new recommended way of configuring what you earlier put in `configDir`, `staticDir` etc. And it also allows you to mount folders in non-Hugo-projects, e.g. the `SCSS` folder in the Bootstrap GitHub project.
* A module consists of a set of mounts to the standard 7 component types in Hugo: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, and `archetypes`. Yes, Theme Components can now include content, which should be very useful, especially in bigger multilingual projects.
* Modules not in your local file cache will be downloaded automatically and even "hot replaced" while the server is running.
* Hugo Modules supports and encourages semver versioned modules, and uses the minimal version selection algorithm to resolve versions.
* A new set of CLI commands are provided to manage all of this: `hugo mod init`, `hugo mod get`, `hugo mod graph`, `hugo mod tidy`, and `hugo mod vendor`.
All of the above is backed by Go Modules.
Fixes #5973
Fixes #5996
Fixes #6010
Fixes #5911
Fixes #5940
Fixes #6074
Fixes #6082
Fixes #6092
2019-05-03 03:16:58 -04:00
n1 o1
o1 n2
n1 n3
project n4
2019-08-10 15:05:17 -04:00
`
c . Assert ( graphb . String ( ) , qt . Equals , expected )
Add Hugo Modules
This commit implements Hugo Modules.
This is a broad subject, but some keywords include:
* A new `module` configuration section where you can import almost anything. You can configure both your own file mounts nd the file mounts of the modules you import. This is the new recommended way of configuring what you earlier put in `configDir`, `staticDir` etc. And it also allows you to mount folders in non-Hugo-projects, e.g. the `SCSS` folder in the Bootstrap GitHub project.
* A module consists of a set of mounts to the standard 7 component types in Hugo: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, and `archetypes`. Yes, Theme Components can now include content, which should be very useful, especially in bigger multilingual projects.
* Modules not in your local file cache will be downloaded automatically and even "hot replaced" while the server is running.
* Hugo Modules supports and encourages semver versioned modules, and uses the minimal version selection algorithm to resolve versions.
* A new set of CLI commands are provided to manage all of this: `hugo mod init`, `hugo mod get`, `hugo mod graph`, `hugo mod tidy`, and `hugo mod vendor`.
All of the above is backed by Go Modules.
Fixes #5973
Fixes #5996
Fixes #6010
Fixes #5911
Fixes #5940
Fixes #6074
Fixes #6082
Fixes #6092
2019-05-03 03:16:58 -04:00
}
2022-06-04 11:39:34 -04:00
func TestInvalidDefaultMarkdownHandler ( t * testing . T ) {
t . Parallel ( )
files := `
-- config . toml --
[ markup ]
defaultMarkdownHandler = ' blackfriday '
-- content / _index . md --
# # Foo
-- layouts / index . html --
{ { . Content } }
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNotNil )
b . Assert ( err . Error ( ) , qt . Contains , "Configured defaultMarkdownHandler \"blackfriday\" not found. Did you mean to use goldmark? Blackfriday was removed in Hugo v0.100.0." )
}
2023-01-15 12:45:51 -05:00
// Issue 8979
func TestHugoConfig ( t * testing . T ) {
filesTemplate := `
-- hugo . toml --
theme = "mytheme"
[ params ]
rootparam = "rootvalue"
-- config / _default / hugo . toml --
[ params ]
rootconfigparam = "rootconfigvalue"
-- themes / mytheme / config / _default / hugo . toml --
[ params ]
themeconfigdirparam = "themeconfigdirvalue"
-- themes / mytheme / hugo . toml --
[ params ]
themeparam = "themevalue"
-- layouts / index . html --
rootparam : { { site . Params . rootparam } }
rootconfigparam : { { site . Params . rootconfigparam } }
themeparam : { { site . Params . themeparam } }
themeconfigdirparam : { { site . Params . themeconfigdirparam } }
`
for _ , configName := range [ ] string { "hugo.toml" , "config.toml" } {
configName := configName
t . Run ( configName , func ( t * testing . T ) {
t . Parallel ( )
files := strings . ReplaceAll ( filesTemplate , "hugo.toml" , configName )
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNil )
b . AssertFileContent ( "public/index.html" ,
"rootparam: rootvalue" ,
"rootconfigparam: rootconfigvalue" ,
"themeparam: themevalue" ,
"themeconfigdirparam: themeconfigdirvalue" ,
)
} )
}
}
2023-05-16 12:53:34 -04:00
2023-05-17 07:12:49 -04:00
func TestConfigOutputFormatDefinedInTheme ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
theme = "mytheme"
[ outputFormats ]
[ outputFormats . myotherformat ]
baseName = ' myotherindex '
mediaType = ' text / html '
[ outputs ]
home = [ ' myformat ' ]
-- themes / mytheme / hugo . toml --
[ outputFormats ]
[ outputFormats . myformat ]
baseName = ' myindex '
mediaType = ' text / html '
-- layouts / index . html --
Home .
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNil )
b . AssertFileContent ( "public/myindex.html" , "Home." )
}
2023-05-17 10:29:06 -04:00
func TestConfigParamSetOnLanguageLevel ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
disableKinds = [ "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" ]
[ languages ]
[ languages . en ]
title = "English Title"
thisIsAParam = "thisIsAParamValue"
[ languages . en . params ]
myparam = "enParamValue"
[ languages . sv ]
title = "Svensk Title"
[ languages . sv . params ]
myparam = "svParamValue"
-- layouts / index . html --
MyParam : { { site . Params . myparam } }
ThisIsAParam : { { site . Params . thisIsAParam } }
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNil )
b . AssertFileContent ( "public/index.html" , `
MyParam : enParamValue
ThisIsAParam : thisIsAParamValue
` )
}
2023-05-16 12:53:34 -04:00
func TestReproCommentsIn10947 ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.com"
2023-05-17 03:59:57 -04:00
disableKinds = [ "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" ]
[ languages ]
[ languages . en ]
2023-05-27 09:44:41 -04:00
languageCode = "en-US"
2023-05-17 03:59:57 -04:00
title = "English Title"
[ languages . en . params ]
myparam = "enParamValue"
[ languages . sv ]
title = "Svensk Title"
[ languages . sv . params ]
myparam = "svParamValue"
-- content / mysection / _index . en . md --
-- -
title : "My English Section"
2023-05-16 12:53:34 -04:00
-- -
2023-05-17 03:59:57 -04:00
-- content / mysection / _index . sv . md --
-- -
title : "My Swedish Section"
2023-05-16 12:53:34 -04:00
-- -
-- layouts / index . html --
2023-05-27 09:44:41 -04:00
LanguageCode : { { eq site . LanguageCode site . Language . LanguageCode } } | { { site . Language . LanguageCode } } |
2023-05-17 03:59:57 -04:00
{ { range $ i , $ e := ( slice site . Site ) } }
2023-05-17 07:12:49 -04:00
{ { $ i } } | AllPages : { { len . AllPages } } | Sections : { { if . Sections } } true { { end } } | Author : { { . Authors } } | BuildDrafts : { { . BuildDrafts } } | IsMultiLingual : { { . IsMultiLingual } } | Param : { { . Language . Params . myparam } } | Language string : { { . Language } } | Languages : { { . Languages } }
2023-05-17 03:59:57 -04:00
{ { end } }
2023-05-16 12:53:34 -04:00
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
2023-05-17 10:29:06 -04:00
{
b . Assert ( b . H . Log . LogCounters ( ) . WarnCounter . Count ( ) , qt . Equals , uint64 ( 2 ) )
}
2023-05-17 03:59:57 -04:00
b . AssertFileContent ( "public/index.html" , `
AllPages : 4 |
Sections : true |
Param : enParamValue
Param : enParamValue
IsMultiLingual : true
2023-05-27 09:44:41 -04:00
LanguageCode : true | en - US |
2023-05-17 03:59:57 -04:00
` )
b . AssertFileContent ( "public/sv/index.html" , `
2023-05-27 09:44:41 -04:00
Param : svParamValue
LanguageCode : true | sv |
2023-05-17 03:59:57 -04:00
` )
2023-05-16 12:53:34 -04:00
}
2023-05-18 09:50:48 -04:00
func TestConfigEmptyMainSections ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . yml --
params :
mainSections :
-- content / mysection / _index . md --
-- content / mysection / mycontent . md --
-- layouts / index . html --
mainSections : { { site . Params . mainSections } }
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . AssertFileContent ( "public/index.html" , `
mainSections : [ ]
` )
}
2023-05-19 03:17:55 -04:00
2023-05-20 09:04:22 -04:00
func TestConfigHugoWorkingDir ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
-- layouts / index . html --
WorkingDir : { { hugo . WorkingDir } } |
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
WorkingDir : "myworkingdir" ,
} ,
) . Build ( )
b . AssertFileContent ( "public/index.html" , `
WorkingDir : myworkingdir |
` )
}
2023-05-20 05:17:43 -04:00
func TestConfigMergeLanguageDeepEmptyLefSide ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
[ params ]
p1 = "p1base"
[ languages . en ]
languageCode = ' en - US '
languageName = ' English '
weight = 1
[ languages . en . markup . goldmark . extensions . typographer ]
leftDoubleQuote = ' & ldquo ; ' # default & ldquo ;
rightDoubleQuote = ' & rdquo ; ' # default & rdquo ;
[ languages . de ]
languageCode = ' de - DE '
languageName = ' Deutsch '
weight = 2
[ languages . de . params ]
p1 = "p1de"
[ languages . de . markup . goldmark . extensions . typographer ]
leftDoubleQuote = ' & laquo ; ' # default & ldquo ;
rightDoubleQuote = ' & raquo ; ' # default & rdquo ;
-- layouts / index . html --
{ { . Content } }
p1 : { { site . Params . p1 } } |
-- content / _index . en . md --
-- -
title : "English Title"
-- -
A "quote" in English .
-- content / _index . de . md --
-- -
title : "Deutsch Title"
-- -
Ein "Zitat" auf Deutsch .
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . AssertFileContent ( "public/index.html" , "p1: p1base" , "<p>A “quote” in English.</p>" )
b . AssertFileContent ( "public/de/index.html" , "p1: p1de" , "<p>Ein «Zitat» auf Deutsch.</p>" )
}
2023-05-19 03:17:55 -04:00
func TestConfigLegacyValues ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
# taxonomyTerm was renamed to term in Hugo 0.60 .0 .
disableKinds = [ "taxonomyTerm" ]
-- layouts / index . html --
Home
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNil )
b . AssertFileContent ( "public/index.html" , `
Home
` )
conf := b . H . Configs . Base
b . Assert ( conf . IsKindEnabled ( "term" ) , qt . Equals , false )
}
2023-05-23 11:39:49 -04:00
// Issue #11000
func TestConfigEmptyTOMLString ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
[ mediaTypes ]
[ mediaTypes . "text/htaccess" ]
suffixes = [ "htaccess" ]
[ outputFormats ]
[ outputFormats . htaccess ]
mediaType = "text/htaccess"
baseName = ""
isPlainText = false
notAlternative = true
-- content / _index . md --
-- -
outputs : [ "html" , "htaccess" ]
-- -
-- layouts / index . html --
HTML .
-- layouts / _default / list . htaccess --
HTACCESS .
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . AssertFileContent ( "public/.htaccess" , "HTACCESS" )
}
2023-05-28 12:42:10 -04:00
func TestConfigLanguageCodeTopLevel ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
languageCode = "en-US"
-- layouts / index . html --
LanguageCode : { { . Site . LanguageCode } } | { { site . Language . LanguageCode } } |
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . AssertFileContent ( "public/index.html" , "LanguageCode: en-US|en-US|" )
}
2023-05-30 05:38:29 -04:00
2023-05-30 05:48:17 -04:00
func TestConfigMiscPanics ( t * testing . T ) {
2023-05-30 05:38:29 -04:00
t . Parallel ( )
2023-05-30 05:48:17 -04:00
// Issue 11047,
t . Run ( "empty params" , func ( t * testing . T ) {
files := `
2023-05-30 05:38:29 -04:00
-- hugo . yaml --
params :
-- layouts / index . html --
Foo : { { site . Params . foo } } |
2023-05-30 05:48:17 -04:00
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
b . AssertFileContent ( "public/index.html" , "Foo: |" )
} )
2023-05-30 05:38:29 -04:00
2023-05-30 05:48:17 -04:00
// Issue 11046
t . Run ( "invalid language setup" , func ( t * testing . T ) {
files := `
-- hugo . toml --
baseURL = "https://example.org"
languageCode = "en-us"
title = "Blog of me"
defaultContentLanguage = "en"
[ languages ]
[ en ]
lang = "en"
languageName = "English"
weight = 1
-- layouts / index . html --
Foo : { { site . Params . foo } } |
2023-05-30 05:38:29 -04:00
2023-05-30 05:48:17 -04:00
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
2023-05-30 05:38:29 -04:00
2023-05-30 05:48:17 -04:00
b . Assert ( err , qt . IsNotNil )
b . Assert ( err . Error ( ) , qt . Contains , "no languages" )
} )
2023-05-30 05:38:29 -04:00
2023-05-30 05:53:38 -04:00
// Issue 11044
t . Run ( "invalid defaultContentLanguage" , func ( t * testing . T ) {
files := `
-- hugo . toml --
baseURL = "https://example.org"
defaultContentLanguage = "sv"
[ languages ]
[ languages . en ]
languageCode = "en"
languageName = "English"
weight = 1
`
b , err := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . BuildE ( )
b . Assert ( err , qt . IsNotNil )
b . Assert ( err . Error ( ) , qt . Contains , "defaultContentLanguage does not match any language definition" )
} )
2023-05-30 05:38:29 -04:00
}
2023-06-01 03:30:16 -04:00
// Issue #11040
func TestConfigModuleDefaultMountsInConfig ( t * testing . T ) {
t . Parallel ( )
files := `
-- hugo . toml --
baseURL = "https://example.org"
contentDir = "mycontent"
-- layouts / index . html --
Home .
`
b := NewIntegrationTestBuilder (
IntegrationTestConfig {
T : t ,
TxtarString : files ,
} ,
) . Build ( )
2023-06-01 03:53:40 -04:00
b . Assert ( b . H . Configs . Base . Module . Mounts , qt . HasLen , 7 )
b . Assert ( b . H . Configs . LanguageConfigSlice [ 0 ] . Module . Mounts , qt . HasLen , 7 )
2023-06-01 03:30:16 -04:00
}