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 }