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:
parent
b14c8c5b44
commit
58d3e66604
20
build.go
20
build.go
@ -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)
|
||||
}
|
||||
}
|
||||
|
25
helpers.go
25
helpers.go
@ -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 {
|
||||
|
@ -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
15
main.go
@ -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
70
page.go
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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
85
site.go
@ -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
5
test/_collection/c1.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
a: b
|
||||
---
|
||||
|
||||
A collection page.
|
4
test/_config.yml
Normal file
4
test/_config.yml
Normal file
@ -0,0 +1,4 @@
|
||||
collections:
|
||||
collection:
|
||||
output: true
|
||||
permalink: /:collection/:path
|
Loading…
Reference in New Issue
Block a user