github.com/gohugoio/hugo@v0.88.1/source/sourceSpec.go (about)

     1  // Copyright 2017-present 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 source
    15  
    16  import (
    17  	"os"
    18  	"path/filepath"
    19  	"regexp"
    20  	"runtime"
    21  
    22  	"github.com/gohugoio/hugo/langs"
    23  	"github.com/spf13/afero"
    24  
    25  	"github.com/gohugoio/hugo/helpers"
    26  	"github.com/spf13/cast"
    27  )
    28  
    29  // SourceSpec abstracts language-specific file creation.
    30  // TODO(bep) rename to Spec
    31  type SourceSpec struct {
    32  	*helpers.PathSpec
    33  
    34  	SourceFs afero.Fs
    35  
    36  	// This is set if the ignoreFiles config is set.
    37  	ignoreFilesRe []*regexp.Regexp
    38  
    39  	Languages              map[string]interface{}
    40  	DefaultContentLanguage string
    41  	DisabledLanguages      map[string]bool
    42  }
    43  
    44  // NewSourceSpec initializes SourceSpec using languages the given filesystem and PathSpec.
    45  func NewSourceSpec(ps *helpers.PathSpec, fs afero.Fs) *SourceSpec {
    46  	cfg := ps.Cfg
    47  	defaultLang := cfg.GetString("defaultContentLanguage")
    48  	languages := cfg.GetStringMap("languages")
    49  
    50  	disabledLangsSet := make(map[string]bool)
    51  
    52  	for _, disabledLang := range cfg.GetStringSlice("disableLanguages") {
    53  		disabledLangsSet[disabledLang] = true
    54  	}
    55  
    56  	if len(languages) == 0 {
    57  		l := langs.NewDefaultLanguage(cfg)
    58  		languages[l.Lang] = l
    59  		defaultLang = l.Lang
    60  	}
    61  
    62  	ignoreFiles := cast.ToStringSlice(cfg.Get("ignoreFiles"))
    63  	var regexps []*regexp.Regexp
    64  	if len(ignoreFiles) > 0 {
    65  		for _, ignorePattern := range ignoreFiles {
    66  			re, err := regexp.Compile(ignorePattern)
    67  			if err != nil {
    68  				helpers.DistinctErrorLog.Printf("Invalid regexp %q in ignoreFiles: %s", ignorePattern, err)
    69  			} else {
    70  				regexps = append(regexps, re)
    71  			}
    72  
    73  		}
    74  	}
    75  
    76  	return &SourceSpec{ignoreFilesRe: regexps, PathSpec: ps, SourceFs: fs, Languages: languages, DefaultContentLanguage: defaultLang, DisabledLanguages: disabledLangsSet}
    77  }
    78  
    79  // IgnoreFile returns whether a given file should be ignored.
    80  func (s *SourceSpec) IgnoreFile(filename string) bool {
    81  	if filename == "" {
    82  		if _, ok := s.SourceFs.(*afero.OsFs); ok {
    83  			return true
    84  		}
    85  		return false
    86  	}
    87  
    88  	base := filepath.Base(filename)
    89  
    90  	if len(base) > 0 {
    91  		first := base[0]
    92  		last := base[len(base)-1]
    93  		if first == '.' ||
    94  			first == '#' ||
    95  			last == '~' {
    96  			return true
    97  		}
    98  	}
    99  
   100  	if len(s.ignoreFilesRe) == 0 {
   101  		return false
   102  	}
   103  
   104  	for _, re := range s.ignoreFilesRe {
   105  		if re.MatchString(filename) {
   106  			return true
   107  		}
   108  	}
   109  
   110  	if runtime.GOOS == "windows" {
   111  		// Also check the forward slash variant if different.
   112  		unixFilename := filepath.ToSlash(filename)
   113  		if unixFilename != filename {
   114  			for _, re := range s.ignoreFilesRe {
   115  				if re.MatchString(unixFilename) {
   116  					return true
   117  				}
   118  			}
   119  		}
   120  	}
   121  
   122  	return false
   123  }
   124  
   125  // IsRegularSourceFile returns whether filename represents a regular file in the
   126  // source filesystem.
   127  func (s *SourceSpec) IsRegularSourceFile(filename string) (bool, error) {
   128  	fi, err := helpers.LstatIfPossible(s.SourceFs, filename)
   129  	if err != nil {
   130  		return false, err
   131  	}
   132  
   133  	if fi.IsDir() {
   134  		return false, nil
   135  	}
   136  
   137  	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
   138  		link, err := filepath.EvalSymlinks(filename)
   139  		if err != nil {
   140  			return false, err
   141  		}
   142  
   143  		fi, err = helpers.LstatIfPossible(s.SourceFs, link)
   144  		if err != nil {
   145  			return false, err
   146  		}
   147  
   148  		if fi.IsDir() {
   149  			return false, nil
   150  		}
   151  	}
   152  
   153  	return true, nil
   154  }