hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

classifier.go (5042B)

    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 files
   15 
   16 import (
   17 	"bufio"
   18 	"fmt"
   19 	"io"
   20 	"os"
   21 	"path/filepath"
   22 	"sort"
   23 	"strings"
   24 	"unicode"
   25 
   26 	"github.com/spf13/afero"
   27 )
   28 
   29 const (
   30 	// The NPM package.json "template" file.
   31 	FilenamePackageHugoJSON = "package.hugo.json"
   32 	// The NPM package file.
   33 	FilenamePackageJSON = "package.json"
   34 )
   35 
   36 var (
   37 	// This should be the only list of valid extensions for content files.
   38 	contentFileExtensions = []string{
   39 		"html", "htm",
   40 		"mdown", "markdown", "md",
   41 		"asciidoc", "adoc", "ad",
   42 		"rest", "rst",
   43 		"org",
   44 		"pandoc", "pdc",
   45 	}
   46 
   47 	contentFileExtensionsSet map[string]bool
   48 
   49 	htmlFileExtensions = []string{
   50 		"html", "htm",
   51 	}
   52 
   53 	htmlFileExtensionsSet map[string]bool
   54 )
   55 
   56 func init() {
   57 	contentFileExtensionsSet = make(map[string]bool)
   58 	for _, ext := range contentFileExtensions {
   59 		contentFileExtensionsSet[ext] = true
   60 	}
   61 	htmlFileExtensionsSet = make(map[string]bool)
   62 	for _, ext := range htmlFileExtensions {
   63 		htmlFileExtensionsSet[ext] = true
   64 	}
   65 }
   66 
   67 func IsContentFile(filename string) bool {
   68 	return contentFileExtensionsSet[strings.TrimPrefix(filepath.Ext(filename), ".")]
   69 }
   70 
   71 func IsIndexContentFile(filename string) bool {
   72 	if !IsContentFile(filename) {
   73 		return false
   74 	}
   75 
   76 	base := filepath.Base(filename)
   77 
   78 	return strings.HasPrefix(base, "index.") || strings.HasPrefix(base, "_index.")
   79 }
   80 
   81 func IsHTMLFile(filename string) bool {
   82 	return htmlFileExtensionsSet[strings.TrimPrefix(filepath.Ext(filename), ".")]
   83 }
   84 
   85 func IsContentExt(ext string) bool {
   86 	return contentFileExtensionsSet[ext]
   87 }
   88 
   89 type ContentClass string
   90 
   91 const (
   92 	ContentClassLeaf    ContentClass = "leaf"
   93 	ContentClassBranch  ContentClass = "branch"
   94 	ContentClassFile    ContentClass = "zfile" // Sort below
   95 	ContentClassContent ContentClass = "zcontent"
   96 )
   97 
   98 func (c ContentClass) IsBundle() bool {
   99 	return c == ContentClassLeaf || c == ContentClassBranch
  100 }
  101 
  102 func ClassifyContentFile(filename string, open func() (afero.File, error)) ContentClass {
  103 	if !IsContentFile(filename) {
  104 		return ContentClassFile
  105 	}
  106 
  107 	if IsHTMLFile(filename) {
  108 		// We need to look inside the file. If the first non-whitespace
  109 		// character is a "<", then we treat it as a regular file.
  110 		// Eearlier we created pages for these files, but that had all sorts
  111 		// of troubles, and isn't what it says in the documentation.
  112 		// See https://github.com/gohugoio/hugo/issues/7030
  113 		if open == nil {
  114 			panic(fmt.Sprintf("no file opener provided for %q", filename))
  115 		}
  116 
  117 		f, err := open()
  118 		if err != nil {
  119 			return ContentClassFile
  120 		}
  121 		ishtml := isHTMLContent(f)
  122 		f.Close()
  123 		if ishtml {
  124 			return ContentClassFile
  125 		}
  126 
  127 	}
  128 
  129 	if strings.HasPrefix(filename, "_index.") {
  130 		return ContentClassBranch
  131 	}
  132 
  133 	if strings.HasPrefix(filename, "index.") {
  134 		return ContentClassLeaf
  135 	}
  136 
  137 	return ContentClassContent
  138 }
  139 
  140 var htmlComment = []rune{'<', '!', '-', '-'}
  141 
  142 func isHTMLContent(r io.Reader) bool {
  143 	br := bufio.NewReader(r)
  144 	i := 0
  145 	for {
  146 		c, _, err := br.ReadRune()
  147 		if err != nil {
  148 			break
  149 		}
  150 
  151 		if i > 0 {
  152 			if i >= len(htmlComment) {
  153 				return false
  154 			}
  155 
  156 			if c != htmlComment[i] {
  157 				return true
  158 			}
  159 
  160 			i++
  161 			continue
  162 		}
  163 
  164 		if !unicode.IsSpace(c) {
  165 			if i == 0 && c != '<' {
  166 				return false
  167 			}
  168 			i++
  169 		}
  170 	}
  171 	return true
  172 }
  173 
  174 const (
  175 	ComponentFolderArchetypes = "archetypes"
  176 	ComponentFolderStatic     = "static"
  177 	ComponentFolderLayouts    = "layouts"
  178 	ComponentFolderContent    = "content"
  179 	ComponentFolderData       = "data"
  180 	ComponentFolderAssets     = "assets"
  181 	ComponentFolderI18n       = "i18n"
  182 
  183 	FolderResources = "resources"
  184 	FolderJSConfig  = "_jsconfig" // Mounted below /assets with postcss.config.js etc.
  185 )
  186 
  187 var (
  188 	JsConfigFolderMountPrefix = filepath.Join(ComponentFolderAssets, FolderJSConfig)
  189 
  190 	ComponentFolders = []string{
  191 		ComponentFolderArchetypes,
  192 		ComponentFolderStatic,
  193 		ComponentFolderLayouts,
  194 		ComponentFolderContent,
  195 		ComponentFolderData,
  196 		ComponentFolderAssets,
  197 		ComponentFolderI18n,
  198 	}
  199 
  200 	componentFoldersSet = make(map[string]bool)
  201 )
  202 
  203 func init() {
  204 	sort.Strings(ComponentFolders)
  205 	for _, f := range ComponentFolders {
  206 		componentFoldersSet[f] = true
  207 	}
  208 }
  209 
  210 // ResolveComponentFolder returns "content" from "content/blog/foo.md" etc.
  211 func ResolveComponentFolder(filename string) string {
  212 	filename = strings.TrimPrefix(filename, string(os.PathSeparator))
  213 	for _, cf := range ComponentFolders {
  214 		if strings.HasPrefix(filename, cf) {
  215 			return cf
  216 		}
  217 	}
  218 
  219 	return ""
  220 }
  221 
  222 func IsComponentFolder(name string) bool {
  223 	return componentFoldersSet[name]
  224 }