summaryrefslogtreecommitdiffstats
path: root/helpers/pathspec.go
diff options
context:
space:
mode:
Diffstat (limited to 'helpers/pathspec.go')
-rw-r--r--helpers/pathspec.go333
1 files changed, 25 insertions, 308 deletions
diff --git a/helpers/pathspec.go b/helpers/pathspec.go
index b18408590..847029f44 100644
--- a/helpers/pathspec.go
+++ b/helpers/pathspec.go
@@ -14,354 +14,71 @@
package helpers
import (
- "fmt"
"strings"
- "github.com/spf13/afero"
-
- "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/hugofs"
- "github.com/spf13/cast"
+ "github.com/gohugoio/hugo/hugolib/filesystems"
+ "github.com/gohugoio/hugo/hugolib/paths"
)
// PathSpec holds methods that decides how paths in URLs and files in Hugo should look like.
type PathSpec struct {
- BaseURL
-
- // If the baseURL contains a base path, e.g. https://example.com/docs, then "/docs" will be the BasePath.
- // This will not be set if canonifyURLs is enabled.
- BasePath string
-
- disablePathToLower bool
- removePathAccents bool
- uglyURLs bool
- canonifyURLs bool
-
- Language *Language
- Languages Languages
-
- // pagination path handling
- paginatePath string
-
- theme string
-
- // Directories
- contentDir string
- themesDir string
- layoutDir string
- workingDir string
- staticDirs []string
- absContentDirs []types.KeyValueStr
-
- PublishDir string
-
- // The PathSpec looks up its config settings in both the current language
- // and then in the global Viper config.
- // Some settings, the settings listed below, does not make sense to be set
- // on per-language-basis. We have no good way of protecting against this
- // other than a "white-list". See language.go.
- defaultContentLanguageInSubdir bool
- defaultContentLanguage string
- multilingual bool
+ *paths.Paths
+ *filesystems.BaseFs
ProcessingStats *ProcessingStats
// The file systems to use
Fs *hugofs.Fs
- // The fine grained filesystems in play (resources, content etc.).
- BaseFs *hugofs.BaseFs
-
// The config provider to use
Cfg config.Provider
}
-func (p PathSpec) String() string {
- return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.Language.Lang, p.getLanguagePrefix(), p.multilingual)
-}
-
-// NewPathSpec creats a new PathSpec from the given filesystems and Language.
+// NewPathSpec creats a new PathSpec from the given filesystems and language.
func NewPathSpec(fs *hugofs.Fs, cfg config.Provider) (*PathSpec, error) {
+ return NewPathSpecWithBaseBaseFsProvided(fs, cfg, nil)
+}
- baseURLstr := cfg.GetString("baseURL")
- baseURL, err := newBaseURLFromString(baseURLstr)
-
- if err != nil {
- return nil, fmt.Errorf("Failed to create baseURL from %q: %s", baseURLstr, err)
- }
-
- var staticDirs []string
-
- for i := -1; i <= 10; i++ {
- staticDirs = append(staticDirs, getStringOrStringSlice(cfg, "staticDir", i)...)
- }
-
- var (
- lang string
- language *Language
- languages Languages
- )
-
- if l, ok := cfg.(*Language); ok {
- language = l
- lang = l.Lang
-
- }
-
- if l, ok := cfg.Get("languagesSorted").(Languages); ok {
- languages = l
- }
-
- defaultContentLanguage := cfg.GetString("defaultContentLanguage")
-
- // We will eventually pull out this badly placed path logic.
- contentDir := cfg.GetString("contentDir")
- workingDir := cfg.GetString("workingDir")
- resourceDir := cfg.GetString("resourceDir")
- publishDir := cfg.GetString("publishDir")
-
- if len(languages) == 0 {
- // We have some old tests that does not test the entire chain, hence
- // they have no languages. So create one so we get the proper filesystem.
- languages = Languages{&Language{Lang: "en", ContentDir: contentDir}}
- }
+// NewPathSpecWithBaseBaseFsProvided creats a new PathSpec from the given filesystems and language.
+// If an existing BaseFs is provided, parts of that is reused.
+func NewPathSpecWithBaseBaseFsProvided(fs *hugofs.Fs, cfg config.Provider, baseBaseFs *filesystems.BaseFs) (*PathSpec, error) {
- absPuslishDir := AbsPathify(workingDir, publishDir)
- if !strings.HasSuffix(absPuslishDir, FilePathSeparator) {
- absPuslishDir += FilePathSeparator
- }
- // If root, remove the second '/'
- if absPuslishDir == "//" {
- absPuslishDir = FilePathSeparator
- }
- absResourcesDir := AbsPathify(workingDir, resourceDir)
- if !strings.HasSuffix(absResourcesDir, FilePathSeparator) {
- absResourcesDir += FilePathSeparator
- }
- if absResourcesDir == "//" {
- absResourcesDir = FilePathSeparator
- }
-
- contentFs, absContentDirs, err := createContentFs(fs.Source, workingDir, defaultContentLanguage, languages)
+ p, err := paths.New(fs, cfg)
if err != nil {
return nil, err
}
- // Make sure we don't have any overlapping content dirs. That will never work.
- for i, d1 := range absContentDirs {
- for j, d2 := range absContentDirs {
- if i == j {
- continue
- }
- if strings.HasPrefix(d1.Value, d2.Value) || strings.HasPrefix(d2.Value, d1.Value) {
- return nil, fmt.Errorf("found overlapping content dirs (%q and %q)", d1, d2)
- }
+ var options []func(*filesystems.BaseFs) error
+ if baseBaseFs != nil {
+ options = []func(*filesystems.BaseFs) error{
+ filesystems.WithBaseFs(baseBaseFs),
}
}
-
- resourcesFs := afero.NewBasePathFs(fs.Source, absResourcesDir)
- publishFs := afero.NewBasePathFs(fs.Destination, absPuslishDir)
-
- baseFs := &hugofs.BaseFs{
- ContentFs: contentFs,
- ResourcesFs: resourcesFs,
- PublishFs: publishFs,
+ bfs, err := filesystems.NewBase(p, options...)
+ if err != nil {
+ return nil, err
}
ps := &PathSpec{
- Fs: fs,
- BaseFs: baseFs,
- Cfg: cfg,
- disablePathToLower: cfg.GetBool("disablePathToLower"),
- removePathAccents: cfg.GetBool("removePathAccents"),
- uglyURLs: cfg.GetBool("uglyURLs"),
- canonifyURLs: cfg.GetBool("canonifyURLs"),
- multilingual: cfg.GetBool("multilingual"),
- Language: language,
- Languages: languages,
- defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"),
- defaultContentLanguage: defaultContentLanguage,
- paginatePath: cfg.GetString("paginatePath"),
- BaseURL: baseURL,
- contentDir: contentDir,
- themesDir: cfg.GetString("themesDir"),
- layoutDir: cfg.GetString("layoutDir"),
- workingDir: workingDir,
- staticDirs: staticDirs,
- absContentDirs: absContentDirs,
- theme: cfg.GetString("theme"),
- ProcessingStats: NewProcessingStats(lang),
+ Paths: p,
+ BaseFs: bfs,
+ Fs: fs,
+ Cfg: cfg,
+ ProcessingStats: NewProcessingStats(p.Lang()),
}
- if !ps.canonifyURLs {
- basePath := ps.BaseURL.url.Path
+ if !ps.CanonifyURLs {
+ basePath := ps.BaseURL.Path()
if basePath != "" && basePath != "/" {
ps.BasePath = basePath
}
}
- // TODO(bep) remove this, eventually
- ps.PublishDir = absPuslishDir
-
return ps, nil
}
-func getStringOrStringSlice(cfg config.Provider, key string, id int) []string {
-
- if id >= 0 {
- key = fmt.Sprintf("%s%d", key, id)
- }
-
- var out []string
-
- sd := cfg.Get(key)
-
- if sds, ok := sd.(string); ok {
- out = []string{sds}
- } else if sd != nil {
- out = cast.ToStringSlice(sd)
- }
-
- return out
-}
-
-func createContentFs(fs afero.Fs,
- workingDir,
- defaultContentLanguage string,
- languages Languages) (afero.Fs, []types.KeyValueStr, error) {
-
- var contentLanguages Languages
- var contentDirSeen = make(map[string]bool)
- languageSet := make(map[string]bool)
-
- // The default content language needs to be first.
- for _, language := range languages {
- if language.Lang == defaultContentLanguage {
- contentLanguages = append(contentLanguages, language)
- contentDirSeen[language.ContentDir] = true
- }
- languageSet[language.Lang] = true
- }
-
- for _, language := range languages {
- if contentDirSeen[language.ContentDir] {
- continue
- }
- if language.ContentDir == "" {
- language.ContentDir = defaultContentLanguage
- }
- contentDirSeen[language.ContentDir] = true
- contentLanguages = append(contentLanguages, language)
-
- }
-
- var absContentDirs []types.KeyValueStr
-
- fs, err := createContentOverlayFs(fs, workingDir, contentLanguages, languageSet, &absContentDirs)
- return fs, absContentDirs, err
-
-}
-
-func createContentOverlayFs(source afero.Fs,
- workingDir string,
- languages Languages,
- languageSet map[string]bool,
- absContentDirs *[]types.KeyValueStr) (afero.Fs, error) {
- if len(languages) == 0 {
- return source, nil
- }
-
- language := languages[0]
-
- contentDir := language.ContentDir
- if contentDir == "" {
- panic("missing contentDir")
- }
-
- absContentDir := AbsPathify(workingDir, language.ContentDir)
- if !strings.HasSuffix(absContentDir, FilePathSeparator) {
- absContentDir += FilePathSeparator
- }
-
- // If root, remove the second '/'
- if absContentDir == "//" {
- absContentDir = FilePathSeparator
- }
-
- if len(absContentDir) < 6 {
- return nil, fmt.Errorf("invalid content dir %q: %s", absContentDir, ErrPathTooShort)
- }
-
- *absContentDirs = append(*absContentDirs, types.KeyValueStr{Key: language.Lang, Value: absContentDir})
-
- overlay := hugofs.NewLanguageFs(language.Lang, languageSet, afero.NewBasePathFs(source, absContentDir))
- if len(languages) == 1 {
- return overlay, nil
- }
-
- base, err := createContentOverlayFs(source, workingDir, languages[1:], languageSet, absContentDirs)
- if err != nil {
- return nil, err
- }
-
- return hugofs.NewLanguageCompositeFs(base, overlay), nil
-
-}
-
-// RelContentDir tries to create a path relative to the content root from
-// the given filename. The return value is the path and language code.
-func (p *PathSpec) RelContentDir(filename string) (string, string) {
- for _, dir := range p.absContentDirs {
- if strings.HasPrefix(filename, dir.Value) {
- rel := strings.TrimPrefix(filename, dir.Value)
- return strings.TrimPrefix(rel, FilePathSeparator), dir.Key
- }
- }
- // Either not a content dir or already relative.
- return filename, ""
-}
-
-// ContentDirs returns all the content dirs (absolute paths).
-func (p *PathSpec) ContentDirs() []types.KeyValueStr {
- return p.absContentDirs
-}
-
-// PaginatePath returns the configured root path used for paginator pages.
-func (p *PathSpec) PaginatePath() string {
- return p.paginatePath
-}
-
-// ContentDir returns the configured workingDir.
-func (p *PathSpec) ContentDir() string {
- return p.contentDir
-}
-
-// WorkingDir returns the configured workingDir.
-func (p *PathSpec) WorkingDir() string {
- return p.workingDir
-}
-
-// StaticDirs returns the relative static dirs for the current configuration.
-func (p *PathSpec) StaticDirs() []string {
- return p.staticDirs
-}
-
-// LayoutDir returns the relative layout dir in the current configuration.
-func (p *PathSpec) LayoutDir() string {
- return p.layoutDir
-}
-
-// Theme returns the theme name if set.
-func (p *PathSpec) Theme() string {
- return p.theme
-}
-
-// Theme returns the theme relative theme dir.
-func (p *PathSpec) ThemesDir() string {
- return p.themesDir
-}
-
// PermalinkForBaseURL creates a permalink from the given link and baseURL.
func (p *PathSpec) PermalinkForBaseURL(link, baseURL string) string {
link = strings.TrimPrefix(link, "/")