github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/common/hugo/hugo.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 hugo 15 16 import ( 17 "fmt" 18 "html/template" 19 "os" 20 "path/filepath" 21 "runtime/debug" 22 "sort" 23 "strings" 24 "sync" 25 26 godartsassv1 "github.com/bep/godartsass" 27 "github.com/mitchellh/mapstructure" 28 29 "time" 30 31 "github.com/bep/godartsass/v2" 32 "github.com/gohugoio/hugo/common/hexec" 33 "github.com/gohugoio/hugo/hugofs/files" 34 35 "github.com/spf13/afero" 36 37 "github.com/gohugoio/hugo/config" 38 "github.com/gohugoio/hugo/hugofs" 39 ) 40 41 const ( 42 EnvironmentDevelopment = "development" 43 EnvironmentProduction = "production" 44 ) 45 46 var ( 47 // buildDate allows vendor-specified build date when .git/ is unavailable. 48 buildDate string 49 // vendorInfo contains vendor notes about the current build. 50 vendorInfo string 51 ) 52 53 // HugoInfo contains information about the current Hugo environment 54 type HugoInfo struct { 55 CommitHash string 56 BuildDate string 57 58 // The build environment. 59 // Defaults are "production" (hugo) and "development" (hugo server). 60 // This can also be set by the user. 61 // It can be any string, but it will be all lower case. 62 Environment string 63 64 // version of go that the Hugo binary was built with 65 GoVersion string 66 67 conf ConfigProvider 68 deps []*Dependency 69 } 70 71 // Version returns the current version as a comparable version string. 72 func (i HugoInfo) Version() VersionString { 73 return CurrentVersion.Version() 74 } 75 76 // Generator a Hugo meta generator HTML tag. 77 func (i HugoInfo) Generator() template.HTML { 78 return template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s">`, CurrentVersion.String())) 79 } 80 81 // IsDevelopment reports whether the current running environment is "development". 82 func (i HugoInfo) IsDevelopment() bool { 83 return i.Environment == EnvironmentDevelopment 84 } 85 86 // IsProduction reports whether the current running environment is "production". 87 func (i HugoInfo) IsProduction() bool { 88 return i.Environment == EnvironmentProduction 89 } 90 91 // IsServer reports whether the built-in server is running. 92 func (i HugoInfo) IsServer() bool { 93 return i.conf.Running() 94 } 95 96 // IsExtended reports whether the Hugo binary is the extended version. 97 func (i HugoInfo) IsExtended() bool { 98 return IsExtended 99 } 100 101 // WorkingDir returns the project working directory. 102 func (i HugoInfo) WorkingDir() string { 103 return i.conf.WorkingDir() 104 } 105 106 // Deps gets a list of dependencies for this Hugo build. 107 func (i HugoInfo) Deps() []*Dependency { 108 return i.deps 109 } 110 111 // ConfigProvider represents the config options that are relevant for HugoInfo. 112 type ConfigProvider interface { 113 Environment() string 114 Running() bool 115 WorkingDir() string 116 } 117 118 // NewInfo creates a new Hugo Info object. 119 func NewInfo(conf ConfigProvider, deps []*Dependency) HugoInfo { 120 if conf.Environment() == "" { 121 panic("environment not set") 122 } 123 var ( 124 commitHash string 125 buildDate string 126 goVersion string 127 ) 128 129 bi := getBuildInfo() 130 if bi != nil { 131 commitHash = bi.Revision 132 buildDate = bi.RevisionTime 133 goVersion = bi.GoVersion 134 } 135 136 return HugoInfo{ 137 CommitHash: commitHash, 138 BuildDate: buildDate, 139 Environment: conf.Environment(), 140 conf: conf, 141 deps: deps, 142 GoVersion: goVersion, 143 } 144 } 145 146 // GetExecEnviron creates and gets the common os/exec environment used in the 147 // external programs we interact with via os/exec, e.g. postcss. 148 func GetExecEnviron(workDir string, cfg config.AllProvider, fs afero.Fs) []string { 149 var env []string 150 nodepath := filepath.Join(workDir, "node_modules") 151 if np := os.Getenv("NODE_PATH"); np != "" { 152 nodepath = workDir + string(os.PathListSeparator) + np 153 } 154 config.SetEnvVars(&env, "NODE_PATH", nodepath) 155 config.SetEnvVars(&env, "PWD", workDir) 156 config.SetEnvVars(&env, "HUGO_ENVIRONMENT", cfg.Environment()) 157 config.SetEnvVars(&env, "HUGO_ENV", cfg.Environment()) 158 config.SetEnvVars(&env, "HUGO_PUBLISHDIR", filepath.Join(workDir, cfg.BaseConfig().PublishDir)) 159 160 if fs != nil { 161 fis, err := afero.ReadDir(fs, files.FolderJSConfig) 162 if err == nil { 163 for _, fi := range fis { 164 key := fmt.Sprintf("HUGO_FILE_%s", strings.ReplaceAll(strings.ToUpper(fi.Name()), ".", "_")) 165 value := fi.(hugofs.FileMetaInfo).Meta().Filename 166 config.SetEnvVars(&env, key, value) 167 } 168 } 169 } 170 171 return env 172 } 173 174 type buildInfo struct { 175 VersionControlSystem string 176 Revision string 177 RevisionTime string 178 Modified bool 179 180 GoOS string 181 GoArch string 182 183 *debug.BuildInfo 184 } 185 186 var bInfo *buildInfo 187 var bInfoInit sync.Once 188 189 func getBuildInfo() *buildInfo { 190 bInfoInit.Do(func() { 191 bi, ok := debug.ReadBuildInfo() 192 if !ok { 193 return 194 } 195 196 bInfo = &buildInfo{BuildInfo: bi} 197 198 for _, s := range bInfo.Settings { 199 switch s.Key { 200 case "vcs": 201 bInfo.VersionControlSystem = s.Value 202 case "vcs.revision": 203 bInfo.Revision = s.Value 204 case "vcs.time": 205 bInfo.RevisionTime = s.Value 206 case "vcs.modified": 207 bInfo.Modified = s.Value == "true" 208 case "GOOS": 209 bInfo.GoOS = s.Value 210 case "GOARCH": 211 bInfo.GoArch = s.Value 212 } 213 } 214 215 }) 216 217 return bInfo 218 } 219 220 func formatDep(path, version string) string { 221 return fmt.Sprintf("%s=%q", path, version) 222 } 223 224 // GetDependencyList returns a sorted dependency list on the format package="version". 225 // It includes both Go dependencies and (a manually maintained) list of C(++) dependencies. 226 func GetDependencyList() []string { 227 var deps []string 228 229 bi := getBuildInfo() 230 if bi == nil { 231 return deps 232 } 233 234 for _, dep := range bi.Deps { 235 deps = append(deps, formatDep(dep.Path, dep.Version)) 236 } 237 238 deps = append(deps, GetDependencyListNonGo()...) 239 240 sort.Strings(deps) 241 242 return deps 243 } 244 245 // GetDependencyListNonGo returns a list of non-Go dependencies. 246 func GetDependencyListNonGo() []string { 247 var deps []string 248 249 if IsExtended { 250 deps = append( 251 deps, 252 formatDep("github.com/sass/libsass", "3.6.5"), 253 formatDep("github.com/webmproject/libwebp", "v1.2.4"), 254 ) 255 } 256 257 if dartSass := dartSassVersion(); dartSass.ProtocolVersion != "" { 258 var dartSassPath = "github.com/sass/dart-sass-embedded" 259 if IsDartSassV2() { 260 dartSassPath = "github.com/sass/dart-sass" 261 } 262 deps = append(deps, 263 formatDep(dartSassPath+"/protocol", dartSass.ProtocolVersion), 264 formatDep(dartSassPath+"/compiler", dartSass.CompilerVersion), 265 formatDep(dartSassPath+"/implementation", dartSass.ImplementationVersion), 266 ) 267 } 268 return deps 269 } 270 271 // IsRunningAsTest reports whether we are running as a test. 272 func IsRunningAsTest() bool { 273 for _, arg := range os.Args { 274 if strings.HasPrefix(arg, "-test") { 275 return true 276 } 277 } 278 return false 279 } 280 281 // Dependency is a single dependency, which can be either a Hugo Module or a local theme. 282 type Dependency struct { 283 // Returns the path to this module. 284 // This will either be the module path, e.g. "github.com/gohugoio/myshortcodes", 285 // or the path below your /theme folder, e.g. "mytheme". 286 Path string 287 288 // The module version. 289 Version string 290 291 // Whether this dependency is vendored. 292 Vendor bool 293 294 // Time version was created. 295 Time time.Time 296 297 // In the dependency tree, this is the first module that defines this module 298 // as a dependency. 299 Owner *Dependency 300 301 // Replaced by this dependency. 302 Replace *Dependency 303 } 304 305 func dartSassVersion() godartsass.DartSassVersion { 306 if DartSassBinaryName == "" { 307 return godartsass.DartSassVersion{} 308 } 309 if IsDartSassV2() { 310 v, _ := godartsass.Version(DartSassBinaryName) 311 return v 312 } 313 314 v, _ := godartsassv1.Version(DartSassBinaryName) 315 var vv godartsass.DartSassVersion 316 mapstructure.WeakDecode(v, &vv) 317 return vv 318 } 319 320 // DartSassBinaryName is the name of the Dart Sass binary to use. 321 // TODO(beop) find a better place for this. 322 var DartSassBinaryName string 323 324 func init() { 325 DartSassBinaryName = os.Getenv("DART_SASS_BINARY") 326 if DartSassBinaryName == "" { 327 for _, name := range dartSassBinaryNamesV2 { 328 if hexec.InPath(name) { 329 DartSassBinaryName = name 330 break 331 } 332 } 333 if DartSassBinaryName == "" { 334 if hexec.InPath(dartSassBinaryNameV1) { 335 DartSassBinaryName = dartSassBinaryNameV1 336 } 337 } 338 } 339 } 340 341 var ( 342 dartSassBinaryNameV1 = "dart-sass-embedded" 343 dartSassBinaryNamesV2 = []string{"dart-sass", "sass"} 344 ) 345 346 func IsDartSassV2() bool { 347 return !strings.Contains(DartSassBinaryName, "embedded") 348 }