github.com/shohhei1126/hugo@v0.42.2-0.20180623210752-3d5928889ad7/output/layout_base.go (about) 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 output 15 16 import ( 17 "fmt" 18 "path/filepath" 19 "strings" 20 21 "github.com/gohugoio/hugo/helpers" 22 ) 23 24 const ( 25 baseFileBase = "baseof" 26 ) 27 28 var ( 29 aceTemplateInnerMarkers = [][]byte{[]byte("= content")} 30 goTemplateInnerMarkers = [][]byte{[]byte("{{define"), []byte("{{ define"), []byte("{{- define"), []byte("{{-define")} 31 ) 32 33 type TemplateNames struct { 34 // The name used as key in the template map. Note that this will be 35 // prefixed with "_text/" if it should be parsed with text/template. 36 Name string 37 38 OverlayFilename string 39 MasterFilename string 40 } 41 42 type TemplateLookupDescriptor struct { 43 // The full path to the site root. 44 WorkingDir string 45 46 // The path to the template relative the the base. 47 // I.e. shortcodes/youtube.html 48 RelPath string 49 50 // The template name prefix to look for. 51 Prefix string 52 53 // All the output formats in play. This is used to decide if text/template or 54 // html/template. 55 OutputFormats Formats 56 57 FileExists func(filename string) (bool, error) 58 ContainsAny func(filename string, subslices [][]byte) (bool, error) 59 } 60 61 func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { 62 63 name := filepath.ToSlash(d.RelPath) 64 name = strings.TrimPrefix(name, "/") 65 66 if d.Prefix != "" { 67 name = strings.Trim(d.Prefix, "/") + "/" + name 68 } 69 70 var ( 71 id TemplateNames 72 ) 73 74 // The filename will have a suffix with an optional type indicator. 75 // Examples: 76 // index.html 77 // index.amp.html 78 // index.json 79 filename := filepath.Base(d.RelPath) 80 isPlainText := false 81 outputFormat, found := d.OutputFormats.FromFilename(filename) 82 83 if found && outputFormat.IsPlainText { 84 isPlainText = true 85 } 86 87 var ext, outFormat string 88 89 parts := strings.Split(filename, ".") 90 if len(parts) > 2 { 91 outFormat = parts[1] 92 ext = parts[2] 93 } else if len(parts) > 1 { 94 ext = parts[1] 95 } 96 97 filenameNoSuffix := parts[0] 98 99 id.OverlayFilename = d.RelPath 100 id.Name = name 101 102 if isPlainText { 103 id.Name = "_text/" + id.Name 104 } 105 106 // Ace and Go templates may have both a base and inner template. 107 pathDir := filepath.Dir(d.RelPath) 108 109 if ext == "amber" || strings.HasSuffix(pathDir, "partials") || strings.HasSuffix(pathDir, "shortcodes") { 110 // No base template support 111 return id, nil 112 } 113 114 innerMarkers := goTemplateInnerMarkers 115 116 var baseFilename string 117 118 if outFormat != "" { 119 baseFilename = fmt.Sprintf("%s.%s.%s", baseFileBase, outFormat, ext) 120 } else { 121 baseFilename = fmt.Sprintf("%s.%s", baseFileBase, ext) 122 } 123 124 if ext == "ace" { 125 innerMarkers = aceTemplateInnerMarkers 126 } 127 128 // This may be a view that shouldn't have base template 129 // Have to look inside it to make sure 130 needsBase, err := d.ContainsAny(d.RelPath, innerMarkers) 131 if err != nil { 132 return id, err 133 } 134 135 if needsBase { 136 currBaseFilename := fmt.Sprintf("%s-%s", filenameNoSuffix, baseFilename) 137 138 // Look for base template in the follwing order: 139 // 1. <current-path>/<template-name>-baseof.<outputFormat>(optional).<suffix>, e.g. list-baseof.<outputFormat>(optional).<suffix>. 140 // 2. <current-path>/baseof.<outputFormat>(optional).<suffix> 141 // 3. _default/<template-name>-baseof.<outputFormat>(optional).<suffix>, e.g. list-baseof.<outputFormat>(optional).<suffix>. 142 // 4. _default/baseof.<outputFormat>(optional).<suffix> 143 // 144 // The filesystem it looks in a a composite of the project and potential theme(s). 145 pathsToCheck := createPathsToCheck(pathDir, baseFilename, currBaseFilename) 146 147 // We may have language code and/or "terms" in the template name. We want the most specific, 148 // but need to fall back to the baseof.html or baseof.ace if needed. 149 // E.g. list-baseof.en.html and list-baseof.terms.en.html 150 // See #3893, #3856. 151 baseBaseFilename, currBaseBaseFilename := helpers.Filename(baseFilename), helpers.Filename(currBaseFilename) 152 p1, p2 := strings.Split(baseBaseFilename, "."), strings.Split(currBaseBaseFilename, ".") 153 if len(p1) > 0 && len(p1) == len(p2) { 154 for i := len(p1); i > 0; i-- { 155 v1, v2 := strings.Join(p1[:i], ".")+"."+ext, strings.Join(p2[:i], ".")+"."+ext 156 pathsToCheck = append(pathsToCheck, createPathsToCheck(pathDir, v1, v2)...) 157 158 } 159 } 160 161 for _, p := range pathsToCheck { 162 if ok, err := d.FileExists(p); err == nil && ok { 163 id.MasterFilename = p 164 break 165 } 166 } 167 } 168 169 return id, nil 170 171 } 172 173 func createPathsToCheck(baseTemplatedDir, baseFilename, currBaseFilename string) []string { 174 return []string{ 175 filepath.Join(baseTemplatedDir, currBaseFilename), 176 filepath.Join(baseTemplatedDir, baseFilename), 177 filepath.Join("_default", currBaseFilename), 178 filepath.Join("_default", baseFilename), 179 } 180 }