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  }