github.com/SuCicada/su-hugo@v1.0.0/hugolib/paths/paths.go (about)

     1  // Copyright 2018 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package paths
    15  
    16  import (
    17  	"fmt"
    18  	"path/filepath"
    19  	"strings"
    20  
    21  	hpaths "github.com/gohugoio/hugo/common/paths"
    22  
    23  	"github.com/gohugoio/hugo/config"
    24  	"github.com/gohugoio/hugo/langs"
    25  	"github.com/gohugoio/hugo/modules"
    26  
    27  	"github.com/gohugoio/hugo/hugofs"
    28  )
    29  
    30  var FilePathSeparator = string(filepath.Separator)
    31  
    32  type Paths struct {
    33  	Fs  *hugofs.Fs
    34  	Cfg config.Provider
    35  
    36  	BaseURL
    37  	BaseURLString       string
    38  	BaseURLNoPathString string
    39  
    40  	// If the baseURL contains a base path, e.g. https://example.com/docs, then "/docs" will be the BasePath.
    41  	BasePath string
    42  
    43  	// Directories
    44  	// TODO(bep) when we have trimmed down most of the dirs usage outside of this package, make
    45  	// these into an interface.
    46  	ThemesDir  string
    47  	WorkingDir string
    48  
    49  	// Directories to store Resource related artifacts.
    50  	AbsResourcesDir string
    51  
    52  	AbsPublishDir string
    53  
    54  	// pagination path handling
    55  	PaginatePath string
    56  
    57  	// When in multihost mode, this returns a list of base paths below PublishDir
    58  	// for each language.
    59  	MultihostTargetBasePaths []string
    60  
    61  	DisablePathToLower bool
    62  	RemovePathAccents  bool
    63  	UglyURLs           bool
    64  	CanonifyURLs       bool
    65  
    66  	Language              *langs.Language
    67  	Languages             langs.Languages
    68  	LanguagesDefaultFirst langs.Languages
    69  
    70  	// The PathSpec looks up its config settings in both the current language
    71  	// and then in the global Viper config.
    72  	// Some settings, the settings listed below, does not make sense to be set
    73  	// on per-language-basis. We have no good way of protecting against this
    74  	// other than a "white-list". See language.go.
    75  	defaultContentLanguageInSubdir bool
    76  	DefaultContentLanguage         string
    77  	multilingual                   bool
    78  
    79  	AllModules    modules.Modules
    80  	ModulesClient *modules.Client
    81  }
    82  
    83  func New(fs *hugofs.Fs, cfg config.Provider) (*Paths, error) {
    84  	baseURLstr := cfg.GetString("baseURL")
    85  	baseURL, err := newBaseURLFromString(baseURLstr)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("Failed to create baseURL from %q:: %w", baseURLstr, err)
    88  	}
    89  
    90  	contentDir := filepath.Clean(cfg.GetString("contentDir"))
    91  	workingDir := filepath.Clean(cfg.GetString("workingDir"))
    92  	resourceDir := filepath.Clean(cfg.GetString("resourceDir"))
    93  	publishDir := filepath.Clean(cfg.GetString("publishDir"))
    94  
    95  	if publishDir == "" {
    96  		return nil, fmt.Errorf("publishDir not set")
    97  	}
    98  
    99  	defaultContentLanguage := cfg.GetString("defaultContentLanguage")
   100  
   101  	var (
   102  		language              *langs.Language
   103  		languages             langs.Languages
   104  		languagesDefaultFirst langs.Languages
   105  	)
   106  
   107  	if l, ok := cfg.(*langs.Language); ok {
   108  		language = l
   109  	}
   110  
   111  	if l, ok := cfg.Get("languagesSorted").(langs.Languages); ok {
   112  		languages = l
   113  	}
   114  
   115  	if l, ok := cfg.Get("languagesSortedDefaultFirst").(langs.Languages); ok {
   116  		languagesDefaultFirst = l
   117  	}
   118  
   119  	//
   120  
   121  	if len(languages) == 0 {
   122  		// We have some old tests that does not test the entire chain, hence
   123  		// they have no languages. So create one so we get the proper filesystem.
   124  		languages = langs.Languages{&langs.Language{Lang: "en", Cfg: cfg, ContentDir: contentDir}}
   125  	}
   126  
   127  	absPublishDir := hpaths.AbsPathify(workingDir, publishDir)
   128  	if !strings.HasSuffix(absPublishDir, FilePathSeparator) {
   129  		absPublishDir += FilePathSeparator
   130  	}
   131  	// If root, remove the second '/'
   132  	if absPublishDir == "//" {
   133  		absPublishDir = FilePathSeparator
   134  	}
   135  	absResourcesDir := hpaths.AbsPathify(workingDir, resourceDir)
   136  	if !strings.HasSuffix(absResourcesDir, FilePathSeparator) {
   137  		absResourcesDir += FilePathSeparator
   138  	}
   139  	if absResourcesDir == "//" {
   140  		absResourcesDir = FilePathSeparator
   141  	}
   142  
   143  	var multihostTargetBasePaths []string
   144  	if languages.IsMultihost() {
   145  		for _, l := range languages {
   146  			multihostTargetBasePaths = append(multihostTargetBasePaths, l.Lang)
   147  		}
   148  	}
   149  
   150  	var baseURLString = baseURL.String()
   151  	var baseURLNoPath = baseURL.URL()
   152  	baseURLNoPath.Path = ""
   153  	var baseURLNoPathString = baseURLNoPath.String()
   154  
   155  	p := &Paths{
   156  		Fs:                  fs,
   157  		Cfg:                 cfg,
   158  		BaseURL:             baseURL,
   159  		BaseURLString:       baseURLString,
   160  		BaseURLNoPathString: baseURLNoPathString,
   161  
   162  		DisablePathToLower: cfg.GetBool("disablePathToLower"),
   163  		RemovePathAccents:  cfg.GetBool("removePathAccents"),
   164  		UglyURLs:           cfg.GetBool("uglyURLs"),
   165  		CanonifyURLs:       cfg.GetBool("canonifyURLs"),
   166  
   167  		ThemesDir:  cfg.GetString("themesDir"),
   168  		WorkingDir: workingDir,
   169  
   170  		AbsResourcesDir: absResourcesDir,
   171  		AbsPublishDir:   absPublishDir,
   172  
   173  		multilingual:                   cfg.GetBool("multilingual"),
   174  		defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"),
   175  		DefaultContentLanguage:         defaultContentLanguage,
   176  
   177  		Language:                 language,
   178  		Languages:                languages,
   179  		LanguagesDefaultFirst:    languagesDefaultFirst,
   180  		MultihostTargetBasePaths: multihostTargetBasePaths,
   181  
   182  		PaginatePath: cfg.GetString("paginatePath"),
   183  	}
   184  
   185  	if cfg.IsSet("allModules") {
   186  		p.AllModules = cfg.Get("allModules").(modules.Modules)
   187  	}
   188  
   189  	if cfg.IsSet("modulesClient") {
   190  		p.ModulesClient = cfg.Get("modulesClient").(*modules.Client)
   191  	}
   192  
   193  	return p, nil
   194  }
   195  
   196  // GetBasePath returns any path element in baseURL if needed.
   197  func (p *Paths) GetBasePath(isRelativeURL bool) string {
   198  	if isRelativeURL && p.CanonifyURLs {
   199  		// The baseURL will be prepended later.
   200  		return ""
   201  	}
   202  	return p.BasePath
   203  }
   204  
   205  func (p *Paths) Lang() string {
   206  	if p == nil || p.Language == nil {
   207  		return ""
   208  	}
   209  	return p.Language.Lang
   210  }
   211  
   212  func (p *Paths) GetTargetLanguageBasePath() string {
   213  	if p.Languages.IsMultihost() {
   214  		// In a multihost configuration all assets will be published below the language code.
   215  		return p.Lang()
   216  	}
   217  	return p.GetLanguagePrefix()
   218  }
   219  
   220  func (p *Paths) GetURLLanguageBasePath() string {
   221  	if p.Languages.IsMultihost() {
   222  		return ""
   223  	}
   224  	return p.GetLanguagePrefix()
   225  }
   226  
   227  func (p *Paths) GetLanguagePrefix() string {
   228  	if !p.multilingual {
   229  		return ""
   230  	}
   231  
   232  	defaultLang := p.DefaultContentLanguage
   233  	defaultInSubDir := p.defaultContentLanguageInSubdir
   234  
   235  	currentLang := p.Language.Lang
   236  	if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
   237  		return ""
   238  	}
   239  	return currentLang
   240  }
   241  
   242  // GetLangSubDir returns the given language's subdir if needed.
   243  func (p *Paths) GetLangSubDir(lang string) string {
   244  	if !p.multilingual {
   245  		return ""
   246  	}
   247  
   248  	if p.Languages.IsMultihost() {
   249  		return ""
   250  	}
   251  
   252  	if lang == "" || (lang == p.DefaultContentLanguage && !p.defaultContentLanguageInSubdir) {
   253  		return ""
   254  	}
   255  
   256  	return lang
   257  }
   258  
   259  // AbsPathify creates an absolute path if given a relative path. If already
   260  // absolute, the path is just cleaned.
   261  func (p *Paths) AbsPathify(inPath string) string {
   262  	return hpaths.AbsPathify(p.WorkingDir, inPath)
   263  }
   264  
   265  // RelPathify trims any WorkingDir prefix from the given filename. If
   266  // the filename is not considered to be absolute, the path is just cleaned.
   267  func (p *Paths) RelPathify(filename string) string {
   268  	filename = filepath.Clean(filename)
   269  	if !filepath.IsAbs(filename) {
   270  		return filename
   271  	}
   272  
   273  	return strings.TrimPrefix(strings.TrimPrefix(filename, p.WorkingDir), FilePathSeparator)
   274  }