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 }