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