1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-26 21:34:45 +01:00

Collection and permalink wip

This commit is contained in:
Oliver Steele 2017-06-10 17:51:46 -04:00
parent b14c8c5b44
commit 58d3e66604
9 changed files with 157 additions and 71 deletions

View File

@ -1,7 +1,6 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@ -10,15 +9,13 @@ import (
func cleanDirectory() error {
removeFiles := func(path string, info os.FileInfo, err error) error {
stat, err := os.Stat(path)
if os.IsNotExist(err) {
return nil
}
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
if stat.IsDir() {
if info.IsDir() {
return nil
}
// TODO check for inclusion in KeepFiles
@ -26,9 +23,10 @@ func cleanDirectory() error {
return err
}
err := filepath.Walk(siteConfig.DestinationDir, removeFiles)
if err == nil {
err = removeEmptyDirectories(siteConfig.DestinationDir)
if err != nil {
return err
}
err = removeEmptyDirectories(siteConfig.DestinationDir)
return err
}
@ -39,7 +37,7 @@ func build() error {
}
for _, page := range siteMap {
if !page.Static {
page, err = readFile(page.Path, true)
page, err = readFile(page.Path, siteData, true)
}
if err != nil {
return err
@ -54,7 +52,7 @@ func build() error {
if page.Static {
os.Link(filepath.Join(siteConfig.SourceDir, page.Path), destPath)
} else {
fmt.Println("render", filepath.Join(siteConfig.SourceDir, page.Path), "->", destPath)
// fmt.Println("render", filepath.Join(siteConfig.SourceDir, page.Path), "->", destPath)
ioutil.WriteFile(destPath, page.Body, 0644)
}
}

View File

@ -1,6 +1,7 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@ -15,6 +16,30 @@ func leftPad(s string, n int) string {
return string(ws) + s
}
func mergeMaps(a map[interface{}]interface{}, b map[interface{}]interface{}) map[interface{}]interface{} {
result := map[interface{}]interface{}{}
for k, v := range a {
result[k] = v
}
for k, v := range b {
result[k] = v
}
return result
}
func stringMap(m map[interface{}]interface{}) map[string]interface{} {
result := map[string]interface{}{}
for k, v := range m {
stringer, ok := k.(fmt.Stringer)
if ok {
result[stringer.String()] = v
} else {
result[fmt.Sprintf("%v", k)] = v
}
}
return result
}
func postfixWalk(path string, walkFn filepath.WalkFunc) error {
files, err := ioutil.ReadDir(path)
if err != nil {

View File

@ -15,7 +15,7 @@ func LinkFactory(p *core.Parser, config *core.Configuration) (core.Tag, error) {
end := p.Position - 2
path := strings.Trim(string(p.Data[start:end]), " ")
permalink, ok := getFilePermalink(path)
permalink, ok := getFileURL(path)
if !ok {
return nil, p.Error(fmt.Sprintf("%s not found", path))
}

15
main.go
View File

@ -33,10 +33,10 @@ func main() {
flag.StringVar(&siteConfig.SourceDir, "source", siteConfig.SourceDir, "Source directory")
flag.Parse()
configPath := filepath.Join(siteConfig.SourceDir, "_siteConfig.yml")
configPath := filepath.Join(siteConfig.SourceDir, "_config.yml")
// TODO error if file is e.g. unreadable
if _, err := os.Stat(configPath); err == nil {
err := siteConfig.readFromDirectory(siteConfig.SourceDir)
err := siteConfig.read(configPath)
if err != nil {
fmt.Println(err)
return
@ -48,7 +48,7 @@ func main() {
printPathSetting("Source:", siteConfig.SourceDir)
printPathSetting("Destination:", siteConfig.DestinationDir)
fileMap, err := buildFileMap()
fileMap, err := buildSiteMap()
if err != nil {
fmt.Println(err)
return
@ -66,11 +66,12 @@ func main() {
printSetting("", fmt.Sprintf("done in %.2fs.", elapsed.Seconds()))
case "routes":
fmt.Printf("\nRoutes:\n")
for urlPath, p := range siteMap {
fmt.Printf(" %s -> %s\n", urlPath, p.Path)
for url, p := range siteMap {
fmt.Printf(" %s -> %s\n", url, p.Path)
}
case "build1":
page, err2 := readFile("index.md", true)
case "render":
// build a single page, and print it to stdout; for testing
page, err2 := readFile("index.md", siteData, true)
if err2 != nil {
err = err2
break

70
page.go
View File

@ -13,7 +13,9 @@ import (
yaml "gopkg.in/yaml.v2"
)
var frontmatterRe = regexp.MustCompile(`(?s)^---\n(.+?)\n---\n`)
var frontmatterMatcher = regexp.MustCompile(`(?s)^---\n(.+?\n)---\n`)
var templateVariableMatcher = regexp.MustCompile(`:(?:collection|path|name|title)\b`)
var nonAlphanumericSequenceMatcher = regexp.MustCompile(`[^[:alnum:]]+`)
// A Page represents an HTML page.
type Page struct {
@ -25,10 +27,11 @@ type Page struct {
}
func (p Page) String() string {
return fmt.Sprintf("Page{Path=%v, Permalink=%v}", p.Path, p.Permalink)
return fmt.Sprintf("Page{Path=%v, Permalink=%v, Static=%v}",
p.Path, p.Permalink, p.Static)
}
func readFile(path string, expand bool) (*Page, error) {
func readFile(path string, defaults map[interface{}]interface{}, expand bool) (*Page, error) {
// TODO don't read, parse binary files
source, err := ioutil.ReadFile(filepath.Join(siteConfig.SourceDir, path))
@ -37,48 +40,51 @@ func readFile(path string, expand bool) (*Page, error) {
}
static := true
data := map[string]interface{}{}
data := defaults
body := source
fmMatchIndex := frontmatterRe.FindSubmatchIndex(source)
if fmMatchIndex != nil {
if match := frontmatterMatcher.FindSubmatchIndex(source); match != nil {
static = false
body = source[fmMatchIndex[1]:]
fmBytes := source[fmMatchIndex[2]:fmMatchIndex[3]]
var fmMap interface{}
err = yaml.Unmarshal(fmBytes, &fmMap)
body = source[match[1]:]
fm := map[interface{}]interface{}{}
err = yaml.Unmarshal(source[match[2]:match[3]], &fm)
if err != nil {
return nil, err
}
fmStringMap, ok := fmMap.(map[interface{}]interface{})
if !ok {
return nil, errors.New("YAML frontmatter is not a map")
}
for k, v := range fmStringMap {
stringer, ok := k.(fmt.Stringer)
if ok {
data[stringer.String()] = v
} else {
data[fmt.Sprintf("%v", k)] = v
}
}
data = mergeMaps(data, fm)
}
ext := filepath.Ext(path)
var title string
if val, ok := data["permalink"]; ok {
title = fmt.Sprintf("%v", val)
} else {
title = filepath.Base(path)
title = title[:len(title)-len(ext)]
}
// var title string
// if val, ok := data["permalink"]; ok {
// title = fmt.Sprintf("%v", val)
// } else {
// title = filepath.Base(path)
// title = title[:len(title)-len(ext)]
// }
// TODO use site, collection default; expand components
permalink := "/" + path[:len(path)-len(ext)]
if val, ok := data["permalink"]; ok {
permalink = val.(string) // TODO what if it's not a string?
permalink, ok = val.(string)
if !ok {
return nil, errors.New("Required string value for permalink")
}
}
templateVariables := map[string]string{}
templateVariables["output_ext"] = ".html"
templateVariables["path"] = regexp.MustCompile(`\.md$`).ReplaceAllLiteralString(path, "")
templateVariables["name"] = nonAlphanumericSequenceMatcher.ReplaceAllString(filepath.Base(path), "-")
if val, found := data["collection"]; found {
collectionName := val.(string)
collectionPath := "_" + collectionName + "/"
templateVariables["collection"] = collectionName
templateVariables["path"] = templateVariables["path"][len(collectionPath):]
}
permalink = templateVariableMatcher.ReplaceAllStringFunc(permalink, func(m string) string {
return templateVariables[m[1:]]
})
if expand && ext == ".md" {
template, err := liquid.Parse(body, nil)
@ -86,7 +92,7 @@ func readFile(path string, expand bool) (*Page, error) {
return nil, err
}
writer := new(bytes.Buffer)
template.Render(writer, data)
template.Render(writer, stringMap(data))
body = blackfriday.MarkdownBasic(writer.Bytes())
}

View File

@ -33,7 +33,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
return
}
p, err := readFile(p.Path, true)
p, err := readFile(p.Path, siteData, true)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Printf("Error expanding %s\n%s", p.Path, err)

85
site.go
View File

@ -1,6 +1,7 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@ -14,9 +15,9 @@ type SiteConfig struct {
Permalink string
SourceDir string
DestinationDir string
// Safe bool
Exclude []string
Include []string
Safe bool
Exclude []string
Include []string
// KeepFiles []string
// TimeZone string
// Encoding string
@ -33,7 +34,9 @@ var siteConfig = SiteConfig{
// A map from URL path -> *Page
var siteMap map[string]*Page
func (config *SiteConfig) readFromDirectory(path string) error {
var siteData = map[interface{}]interface{}{}
func (config *SiteConfig) read(path string) error {
configBytes, err := ioutil.ReadFile(path)
if err == nil {
err = yaml.Unmarshal(configBytes, &config)
@ -43,7 +46,7 @@ func (config *SiteConfig) readFromDirectory(path string) error {
return err
}
func buildFileMap() (map[string]*Page, error) {
func buildSiteMap() (map[string]*Page, error) {
basePath := siteConfig.SourceDir
fileMap := map[string]*Page{}
exclusionMap := stringArrayToMap(siteConfig.Exclude)
@ -52,14 +55,9 @@ func buildFileMap() (map[string]*Page, error) {
if err != nil {
return err
}
if path == siteConfig.SourceDir {
if path == basePath {
return nil
}
// TODO replace by info.IsDir
stat, err := os.Stat(path)
if err != nil {
return err
}
relPath, err := filepath.Rel(basePath, path)
if err != nil {
@ -70,25 +68,74 @@ func buildFileMap() (map[string]*Page, error) {
_, exclude := exclusionMap[relPath]
exclude = exclude || strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_")
if exclude {
if stat.IsDir() {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
if !stat.IsDir() {
page, err := readFile(relPath, false)
if err != nil {
return err
}
fileMap[page.Permalink] = page
if info.IsDir() {
return nil
}
p, err := readFile(relPath, siteData, false)
if err != nil {
return err
}
fileMap[p.Permalink] = p
return nil
}
err := filepath.Walk(basePath, walkFn)
if err != nil {
return nil, err
}
for name, colVal := range siteConfig.Collections {
data := colVal.(map[interface{}]interface{})
output := false
if val, found := data["output"]; found {
output = val.(bool)
}
if output {
err = addCollectionFiles(fileMap, name, data)
if err != nil {
return nil, err
}
}
}
return fileMap, err
}
func getFilePermalink(path string) (string, bool) {
func addCollectionFiles(fileMap map[string]*Page, name string, data map[interface{}]interface{}) error {
basePath := siteConfig.SourceDir
collData := mergeMaps(siteData, data)
collData["collection"] = name
walkFn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel(basePath, path)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
p, err := readFile(relPath, collData, false)
if err != nil {
return err
}
if p.Static {
fmt.Printf("skipping static file inside collection: %s\n", path)
} else {
fileMap[p.Permalink] = p
}
return nil
}
err := filepath.Walk(filepath.Join(basePath, "_"+name), walkFn)
return err
}
func getFileURL(path string) (string, bool) {
for _, v := range siteMap {
if v.Path == path {
return v.Permalink, true

5
test/_collection/c1.md Normal file
View File

@ -0,0 +1,5 @@
---
a: b
---
A collection page.

4
test/_config.yml Normal file
View File

@ -0,0 +1,4 @@
collections:
collection:
output: true
permalink: /:collection/:path