github.com/olliephillips/hugo@v0.42.2/hugolib/page_collections.go (about) 1 // Copyright 2016 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 hugolib 15 16 import ( 17 "path" 18 "path/filepath" 19 "strings" 20 21 "github.com/gohugoio/hugo/cache" 22 "github.com/gohugoio/hugo/helpers" 23 ) 24 25 // PageCollections contains the page collections for a site. 26 type PageCollections struct { 27 // Includes only pages of all types, and only pages in the current language. 28 Pages Pages 29 30 // Includes all pages in all languages, including the current one. 31 // Includes pages of all types. 32 AllPages Pages 33 34 // A convenience cache for the traditional index types, taxonomies, home page etc. 35 // This is for the current language only. 36 indexPages Pages 37 38 // A convenience cache for the regular pages. 39 // This is for the current language only. 40 RegularPages Pages 41 42 // A convenience cache for the all the regular pages. 43 AllRegularPages Pages 44 45 // Includes absolute all pages (of all types), including drafts etc. 46 rawAllPages Pages 47 48 // Includes headless bundles, i.e. bundles that produce no output for its content page. 49 headlessPages Pages 50 51 pageCache *cache.PartitionedLazyCache 52 } 53 54 func (c *PageCollections) refreshPageCaches() { 55 c.indexPages = c.findPagesByKindNotIn(KindPage, c.Pages) 56 c.RegularPages = c.findPagesByKindIn(KindPage, c.Pages) 57 c.AllRegularPages = c.findPagesByKindIn(KindPage, c.AllPages) 58 59 var s *Site 60 61 if len(c.Pages) > 0 { 62 s = c.Pages[0].s 63 } 64 65 cacheLoader := func(kind string) func() (map[string]interface{}, error) { 66 return func() (map[string]interface{}, error) { 67 cache := make(map[string]interface{}) 68 switch kind { 69 case KindPage: 70 // Note that we deliberately use the pages from all sites 71 // in this cache, as we intend to use this in the ref and relref 72 // shortcodes. If the user says "sect/doc1.en.md", he/she knows 73 // what he/she is looking for. 74 for _, pageCollection := range []Pages{c.AllRegularPages, c.headlessPages} { 75 for _, p := range pageCollection { 76 cache[filepath.ToSlash(p.Source.Path())] = p 77 78 if s != nil && p.s == s { 79 // Ref/Relref supports this potentially ambiguous lookup. 80 cache[p.Source.LogicalName()] = p 81 82 translasionBaseName := p.Source.TranslationBaseName() 83 dir := filepath.ToSlash(strings.TrimSuffix(p.Dir(), helpers.FilePathSeparator)) 84 85 if translasionBaseName == "index" { 86 _, name := path.Split(dir) 87 cache[name] = p 88 cache[dir] = p 89 } else { 90 // Again, ambigous 91 cache[translasionBaseName] = p 92 } 93 94 // We need a way to get to the current language version. 95 pathWithNoExtensions := path.Join(dir, translasionBaseName) 96 cache[pathWithNoExtensions] = p 97 } 98 } 99 100 } 101 default: 102 for _, p := range c.indexPages { 103 key := path.Join(p.sections...) 104 cache[key] = p 105 } 106 } 107 108 return cache, nil 109 } 110 } 111 112 partitions := make([]cache.Partition, len(allKindsInPages)) 113 114 for i, kind := range allKindsInPages { 115 partitions[i] = cache.Partition{Key: kind, Load: cacheLoader(kind)} 116 } 117 118 c.pageCache = cache.NewPartitionedLazyCache(partitions...) 119 } 120 121 func newPageCollections() *PageCollections { 122 return &PageCollections{} 123 } 124 125 func newPageCollectionsFromPages(pages Pages) *PageCollections { 126 return &PageCollections{rawAllPages: pages} 127 } 128 129 func (c *PageCollections) getPage(typ string, sections ...string) *Page { 130 var key string 131 if len(sections) == 1 { 132 key = filepath.ToSlash(sections[0]) 133 } else { 134 key = path.Join(sections...) 135 } 136 137 p, _ := c.pageCache.Get(typ, key) 138 if p == nil { 139 return nil 140 } 141 return p.(*Page) 142 143 } 144 145 func (*PageCollections) findPagesByKindIn(kind string, inPages Pages) Pages { 146 var pages Pages 147 for _, p := range inPages { 148 if p.Kind == kind { 149 pages = append(pages, p) 150 } 151 } 152 return pages 153 } 154 155 func (*PageCollections) findFirstPageByKindIn(kind string, inPages Pages) *Page { 156 for _, p := range inPages { 157 if p.Kind == kind { 158 return p 159 } 160 } 161 return nil 162 } 163 164 func (*PageCollections) findPagesByKindNotIn(kind string, inPages Pages) Pages { 165 var pages Pages 166 for _, p := range inPages { 167 if p.Kind != kind { 168 pages = append(pages, p) 169 } 170 } 171 return pages 172 } 173 174 func (c *PageCollections) findPagesByKind(kind string) Pages { 175 return c.findPagesByKindIn(kind, c.Pages) 176 } 177 178 func (c *PageCollections) addPage(page *Page) { 179 c.rawAllPages = append(c.rawAllPages, page) 180 } 181 182 func (c *PageCollections) removePageFilename(filename string) { 183 if i := c.rawAllPages.findPagePosByFilename(filename); i >= 0 { 184 c.clearResourceCacheForPage(c.rawAllPages[i]) 185 c.rawAllPages = append(c.rawAllPages[:i], c.rawAllPages[i+1:]...) 186 } 187 188 } 189 190 func (c *PageCollections) removePage(page *Page) { 191 if i := c.rawAllPages.findPagePos(page); i >= 0 { 192 c.clearResourceCacheForPage(c.rawAllPages[i]) 193 c.rawAllPages = append(c.rawAllPages[:i], c.rawAllPages[i+1:]...) 194 } 195 196 } 197 198 func (c *PageCollections) findPagesByShortcode(shortcode string) Pages { 199 var pages Pages 200 201 for _, p := range c.rawAllPages { 202 if p.shortcodeState != nil { 203 if _, ok := p.shortcodeState.nameSet[shortcode]; ok { 204 pages = append(pages, p) 205 } 206 } 207 } 208 return pages 209 } 210 211 func (c *PageCollections) replacePage(page *Page) { 212 // will find existing page that matches filepath and remove it 213 c.removePage(page) 214 c.addPage(page) 215 } 216 217 func (c *PageCollections) clearResourceCacheForPage(page *Page) { 218 if len(page.Resources) > 0 { 219 first := page.Resources[0] 220 dir := path.Dir(first.RelPermalink()) 221 dir = strings.TrimPrefix(dir, page.LanguagePrefix()) 222 // This is done to keep the memory usage in check when doing live reloads. 223 page.s.resourceSpec.DeleteCacheByPrefix(dir) 224 } 225 }