URI: 
       config.go - hugo - [fork] hugo port for 9front
  HTML git clone https://git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       config.go (12053B)
       ---
            1 // Copyright 2019 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 modules
           15 
           16 import (
           17         "fmt"
           18         "os"
           19         "path/filepath"
           20         "strings"
           21 
           22         "github.com/gohugoio/hugo/common/hugo"
           23         "github.com/gohugoio/hugo/hugofs/files"
           24 
           25         "github.com/gohugoio/hugo/config"
           26         "github.com/mitchellh/mapstructure"
           27 )
           28 
           29 const WorkspaceDisabled = "off"
           30 
           31 var DefaultModuleConfig = Config{
           32         // Default to direct, which means "git clone" and similar. We
           33         // will investigate proxy settings in more depth later.
           34         // See https://github.com/golang/go/issues/26334
           35         Proxy: "direct",
           36 
           37         // Comma separated glob list matching paths that should not use the
           38         // proxy configured above.
           39         NoProxy: "none",
           40 
           41         // Comma separated glob list matching paths that should be
           42         // treated as private.
           43         Private: "*.*",
           44 
           45         // Default is no workspace resolution.
           46         Workspace: WorkspaceDisabled,
           47 
           48         // A list of replacement directives mapping a module path to a directory
           49         // or a theme component in the themes folder.
           50         // Note that this will turn the component into a traditional theme component
           51         // that does not partake in vendoring etc.
           52         // The syntax is the similar to the replacement directives used in go.mod, e.g:
           53         //    github.com/mod1 -> ../mod1,github.com/mod2 -> ../mod2
           54         Replacements: nil,
           55 }
           56 
           57 // ApplyProjectConfigDefaults applies default/missing module configuration for
           58 // the main project.
           59 func ApplyProjectConfigDefaults(mod Module, cfgs ...config.AllProvider) error {
           60         moda := mod.(*moduleAdapter)
           61 
           62         // To bridge between old and new configuration format we need
           63         // a way to make sure all of the core components are configured on
           64         // the basic level.
           65         componentsConfigured := make(map[string]bool)
           66         for _, mnt := range moda.mounts {
           67                 if !strings.HasPrefix(mnt.Target, files.JsConfigFolderMountPrefix) {
           68                         componentsConfigured[mnt.Component()] = true
           69                 }
           70         }
           71 
           72         var mounts []Mount
           73 
           74         for _, component := range []string{
           75                 files.ComponentFolderContent,
           76                 files.ComponentFolderData,
           77                 files.ComponentFolderLayouts,
           78                 files.ComponentFolderI18n,
           79                 files.ComponentFolderArchetypes,
           80                 files.ComponentFolderAssets,
           81                 files.ComponentFolderStatic,
           82         } {
           83                 if componentsConfigured[component] {
           84                         continue
           85                 }
           86 
           87                 first := cfgs[0]
           88                 dirsBase := first.DirsBase()
           89                 isMultihost := first.IsMultihost()
           90 
           91                 for i, cfg := range cfgs {
           92                         dirs := cfg.Dirs()
           93                         var dir string
           94                         var dropLang bool
           95                         switch component {
           96                         case files.ComponentFolderContent:
           97                                 dir = dirs.ContentDir
           98                                 dropLang = dir == dirsBase.ContentDir
           99                         case files.ComponentFolderData:
          100                                 //lint:ignore SA1019 Keep as adapter for now.
          101                                 dir = dirs.DataDir
          102                         case files.ComponentFolderLayouts:
          103                                 //lint:ignore SA1019 Keep as adapter for now.
          104                                 dir = dirs.LayoutDir
          105                         case files.ComponentFolderI18n:
          106                                 //lint:ignore SA1019 Keep as adapter for now.
          107                                 dir = dirs.I18nDir
          108                         case files.ComponentFolderArchetypes:
          109                                 //lint:ignore SA1019 Keep as adapter for now.
          110                                 dir = dirs.ArcheTypeDir
          111                         case files.ComponentFolderAssets:
          112                                 //lint:ignore SA1019 Keep as adapter for now.
          113                                 dir = dirs.AssetDir
          114                         case files.ComponentFolderStatic:
          115                                 // For static dirs, we only care about the language in multihost setups.
          116                                 dropLang = !isMultihost
          117                         }
          118 
          119                         var perLang bool
          120                         switch component {
          121                         case files.ComponentFolderContent, files.ComponentFolderStatic:
          122                                 perLang = true
          123                         default:
          124                         }
          125                         if i > 0 && !perLang {
          126                                 continue
          127                         }
          128 
          129                         var lang string
          130                         if perLang && !dropLang {
          131                                 lang = cfg.Language().Lang
          132                         }
          133 
          134                         // Static mounts are a little special.
          135                         if component == files.ComponentFolderStatic {
          136                                 staticDirs := cfg.StaticDirs()
          137                                 for _, dir := range staticDirs {
          138                                         mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: component})
          139                                 }
          140                                 continue
          141                         }
          142 
          143                         if dir != "" {
          144                                 mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: component})
          145                         }
          146                 }
          147         }
          148 
          149         moda.mounts = append(moda.mounts, mounts...)
          150 
          151         // Temporary: Remove duplicates.
          152         seen := make(map[string]bool)
          153         var newMounts []Mount
          154         for _, m := range moda.mounts {
          155                 key := m.Source + m.Target + m.Lang
          156                 if seen[key] {
          157                         continue
          158                 }
          159                 seen[key] = true
          160                 newMounts = append(newMounts, m)
          161         }
          162         moda.mounts = newMounts
          163 
          164         return nil
          165 }
          166 
          167 // DecodeConfig creates a modules Config from a given Hugo configuration.
          168 func DecodeConfig(cfg config.Provider) (Config, error) {
          169         return decodeConfig(cfg, nil)
          170 }
          171 
          172 func decodeConfig(cfg config.Provider, pathReplacements map[string]string) (Config, error) {
          173         c := DefaultModuleConfig
          174         c.replacementsMap = pathReplacements
          175 
          176         if cfg == nil {
          177                 return c, nil
          178         }
          179 
          180         themeSet := cfg.IsSet("theme")
          181         moduleSet := cfg.IsSet("module")
          182 
          183         if moduleSet {
          184                 m := cfg.GetStringMap("module")
          185                 if err := mapstructure.WeakDecode(m, &c); err != nil {
          186                         return c, err
          187                 }
          188 
          189                 if c.replacementsMap == nil {
          190 
          191                         if len(c.Replacements) == 1 {
          192                                 c.Replacements = strings.Split(c.Replacements[0], ",")
          193                         }
          194 
          195                         for i, repl := range c.Replacements {
          196                                 c.Replacements[i] = strings.TrimSpace(repl)
          197                         }
          198 
          199                         c.replacementsMap = make(map[string]string)
          200                         for _, repl := range c.Replacements {
          201                                 parts := strings.Split(repl, "->")
          202                                 if len(parts) != 2 {
          203                                         return c, fmt.Errorf(`invalid module.replacements: %q; configure replacement pairs on the form "oldpath->newpath" `, repl)
          204                                 }
          205 
          206                                 c.replacementsMap[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
          207                         }
          208                 }
          209 
          210                 if c.replacementsMap != nil && c.Imports != nil {
          211                         for i, imp := range c.Imports {
          212                                 if newImp, found := c.replacementsMap[imp.Path]; found {
          213                                         imp.Path = newImp
          214                                         imp.pathProjectReplaced = true
          215                                         c.Imports[i] = imp
          216                                 }
          217                         }
          218                 }
          219 
          220                 for i, mnt := range c.Mounts {
          221                         mnt.Source = filepath.Clean(mnt.Source)
          222                         mnt.Target = filepath.Clean(mnt.Target)
          223                         c.Mounts[i] = mnt
          224                 }
          225 
          226                 if c.Workspace == "" {
          227                         c.Workspace = WorkspaceDisabled
          228                 }
          229                 if c.Workspace != WorkspaceDisabled {
          230                         c.Workspace = filepath.Clean(c.Workspace)
          231                         if !filepath.IsAbs(c.Workspace) {
          232                                 workingDir := cfg.GetString("workingDir")
          233                                 c.Workspace = filepath.Join(workingDir, c.Workspace)
          234                         }
          235                         if _, err := os.Stat(c.Workspace); err != nil {
          236                                 //lint:ignore ST1005 end user message.
          237                                 return c, fmt.Errorf("module workspace %q does not exist. Check your module.workspace setting (or HUGO_MODULE_WORKSPACE env var).", c.Workspace)
          238                         }
          239                 }
          240         }
          241 
          242         if themeSet {
          243                 imports := config.GetStringSlicePreserveString(cfg, "theme")
          244                 for _, imp := range imports {
          245                         c.Imports = append(c.Imports, Import{
          246                                 Path: imp,
          247                         })
          248                 }
          249         }
          250 
          251         return c, nil
          252 }
          253 
          254 // Config holds a module config.
          255 type Config struct {
          256         // File system mounts.
          257         Mounts []Mount
          258 
          259         // Module imports.
          260         Imports []Import
          261 
          262         // Meta info about this module (license information etc.).
          263         Params map[string]any
          264 
          265         // Will be validated against the running Hugo version.
          266         HugoVersion HugoVersion
          267 
          268         // Optional Glob pattern matching module paths to skip when vendoring, e.g. “github.com/**”
          269         NoVendor string
          270 
          271         // When enabled, we will pick the vendored module closest to the module
          272         // using it.
          273         // The default behavior is to pick the first.
          274         // Note that there can still be only one dependency of a given module path,
          275         // so once it is in use it cannot be redefined.
          276         VendorClosest bool
          277 
          278         // A comma separated (or a slice) list of module path to directory replacement mapping,
          279         // e.g. github.com/bep/my-theme -> ../..,github.com/bep/shortcodes -> /some/path.
          280         // This is mostly useful for temporary locally development of a module, and then it makes sense to set it as an
          281         // OS environment variable, e.g: env HUGO_MODULE_REPLACEMENTS="github.com/bep/my-theme -> ../..".
          282         // Any relative path is relate to themesDir, and absolute paths are allowed.
          283         Replacements    []string
          284         replacementsMap map[string]string
          285 
          286         // Defines the proxy server to use to download remote modules. Default is direct, which means “git clone” and similar.
          287         // Configures GOPROXY when running the Go command for module operations.
          288         Proxy string
          289 
          290         // Comma separated glob list matching paths that should not use the proxy configured above.
          291         // Configures GONOPROXY when running the Go command for module operations.
          292         NoProxy string
          293 
          294         // Comma separated glob list matching paths that should be treated as private.
          295         // Configures GOPRIVATE when running the Go command for module operations.
          296         Private string
          297 
          298         // Configures GOAUTH when running the Go command for module operations.
          299         // This is a semicolon-separated list of authentication commands for go-import and HTTPS module mirror interactions.
          300         // This is useful for private repositories.
          301         // See `go help goauth` for more information.
          302         Auth string
          303 
          304         // Defaults to "off".
          305         // Set to a work file, e.g. hugo.work, to enable Go "Workspace" mode.
          306         // Can be relative to the working directory or absolute.
          307         // Requires Go 1.18+.
          308         // Note that this can also be set via OS env, e.g. export HUGO_MODULE_WORKSPACE=/my/hugo.work.
          309         Workspace string
          310 }
          311 
          312 // hasModuleImport reports whether the project config have one or more
          313 // modules imports, e.g. github.com/bep/myshortcodes.
          314 func (c Config) hasModuleImport() bool {
          315         for _, imp := range c.Imports {
          316                 if isProbablyModule(imp.Path) {
          317                         return true
          318                 }
          319         }
          320         return false
          321 }
          322 
          323 // HugoVersion holds Hugo binary version requirements for a module.
          324 type HugoVersion struct {
          325         // The minimum Hugo version that this module works with.
          326         Min hugo.VersionString
          327 
          328         // The maximum Hugo version that this module works with.
          329         Max hugo.VersionString
          330 
          331         // Set if the extended version is needed.
          332         Extended bool
          333 }
          334 
          335 func (v HugoVersion) String() string {
          336         extended := ""
          337         if v.Extended {
          338                 extended = " extended"
          339         }
          340 
          341         if v.Min != "" && v.Max != "" {
          342                 return fmt.Sprintf("%s/%s%s", v.Min, v.Max, extended)
          343         }
          344 
          345         if v.Min != "" {
          346                 return fmt.Sprintf("Min %s%s", v.Min, extended)
          347         }
          348 
          349         if v.Max != "" {
          350                 return fmt.Sprintf("Max %s%s", v.Max, extended)
          351         }
          352 
          353         return extended
          354 }
          355 
          356 // IsValid reports whether this version is valid compared to the running
          357 // Hugo binary.
          358 func (v HugoVersion) IsValid() bool {
          359         current := hugo.CurrentVersion.Version()
          360         if v.Extended && !hugo.IsExtended {
          361                 return false
          362         }
          363 
          364         isValid := true
          365 
          366         if v.Min != "" && current.Compare(v.Min) > 0 {
          367                 isValid = false
          368         }
          369 
          370         if v.Max != "" && current.Compare(v.Max) < 0 {
          371                 isValid = false
          372         }
          373 
          374         return isValid
          375 }
          376 
          377 type Import struct {
          378         // Module path
          379         Path string
          380         // Set when Path is replaced in project config.
          381         pathProjectReplaced bool
          382         // Ignore any config in config.toml (will still follow imports).
          383         IgnoreConfig bool
          384         // Do not follow any configured imports.
          385         IgnoreImports bool
          386         // Do not mount any folder in this import.
          387         NoMounts bool
          388         // Never vendor this import (only allowed in main project).
          389         NoVendor bool
          390         // Turn off this module.
          391         Disable bool
          392         // File mounts.
          393         Mounts []Mount
          394 }
          395 
          396 type Mount struct {
          397         // Relative path in source repo, e.g. "scss".
          398         Source string
          399 
          400         // Relative target path, e.g. "assets/bootstrap/scss".
          401         Target string
          402 
          403         // Any file in this mount will be associated with this language.
          404         Lang string
          405 
          406         // Include only files matching the given Glob patterns (string or slice).
          407         IncludeFiles any
          408 
          409         // Exclude all files matching the given Glob patterns (string or slice).
          410         ExcludeFiles any
          411 
          412         // Disable watching in watch mode for this mount.
          413         DisableWatch bool
          414 }
          415 
          416 // Used as key to remove duplicates.
          417 func (m Mount) key() string {
          418         return strings.Join([]string{m.Lang, m.Source, m.Target}, "/")
          419 }
          420 
          421 func (m Mount) Component() string {
          422         return strings.Split(m.Target, fileSeparator)[0]
          423 }
          424 
          425 func (m Mount) ComponentAndName() (string, string) {
          426         c, n, _ := strings.Cut(m.Target, fileSeparator)
          427         return c, n
          428 }