Remove titles and dates from pages

This commit is contained in:
adnano 2020-09-22 21:11:56 -04:00
parent d0713476c0
commit e00ca65aee
3 changed files with 178 additions and 104 deletions

View file

@ -26,11 +26,11 @@ kiln
A kiln site is organized in the following way:
```
src/ Site source
templates/ Templates
page.gmi Page template
section.gmi Section template
dst/ Site destination
src/ Site source
templates/ Templates
page.gmi Page template
directory.gmi Directory template
dst/ Site destination
```
Running `kiln` takes the contents in `src`, runs them through the templates in
@ -46,13 +46,14 @@ Page templates are provided with the following information:
- `Permalink`: Permalink to the page
- `Content`: The contents of the file (including the title)
## Sections
## Directories
Section templates are provided with the following information:
Directory templates are provided with the following information:
- `Path`: Relative path to the section
- `Permalink`: Permalink to the section
- `Pages`: The pages in this section
- `Path`: Relative path to the directory
- `Permalink`: Permalink to the directory
- `Pages`: The pages in this directory
- `Directories`: The subdirectories of this directory
## Templates
@ -61,4 +62,4 @@ Templates are located in the `templates` directory.
There are currently two supported templates:
- `page.gmi`: The template used for pages
- `section.gmi`: The template used for sections
- `directory.gmi`: The template used for directories

257
kiln.go
View file

@ -11,12 +11,13 @@ import (
)
type Site struct {
Files map[string][]byte
Dirs map[string][]string
Pages map[string]*Page
Templates *template.Template
Static map[string][]byte // Static files
Directory *Directory // Site directory
Templates *template.Template // Templates
}
// LoadSite loads and returns a Site.
// It reads site content from the specified source directory.
func LoadSite(srcDir string) (*Site, error) {
tmpl, err := template.New("templates").ParseGlob("templates/*.gmi")
if err != nil {
@ -24,58 +25,31 @@ func LoadSite(srcDir string) (*Site, error) {
}
site := &Site{
Files: map[string][]byte{},
Dirs: map[string][]string{},
Pages: map[string]*Page{},
Static: map[string][]byte{},
Directory: NewDirectory(""),
Templates: tmpl,
}
if err := site.ReadDir(srcDir, ""); err != nil {
if err := site.Directory.Read(site, srcDir, ""); err != nil {
return nil, err
}
return site, nil
}
// ReadDir reads a directory and indexes the files and directories within it.
func (s *Site) ReadDir(srcDir string, dstDir string) error {
entries, err := ioutil.ReadDir(srcDir)
if err != nil {
return err
}
for _, entry := range entries {
name := entry.Name()
srcPath := filepath.Join(srcDir, name)
dstPath := filepath.Join(dstDir, name)
if entry.IsDir() {
s.Dirs[dstPath] = []string{}
s.ReadDir(srcPath, dstPath)
} else {
content, err := ioutil.ReadFile(srcPath)
if err != nil {
return err
}
s.Files[dstPath] = content
s.Dirs[dstDir] = append(s.Dirs[dstDir], dstPath)
}
}
return nil
}
// Write writes the contents of the Index to the provided destination directory.
func (s *Site) Write(dstDir string) error {
// Empty the destination directory
if err := os.RemoveAll(dstDir); err != nil {
return err
}
// Create the destination directory
if err := os.MkdirAll(dstDir, 0755); err != nil {
return err
}
for path := range s.Files {
// Write the static files
for path := range s.Static {
// Create any parent directories
if dir := filepath.Dir(path); dir != "." {
dstPath := filepath.Join(dstDir, dir)
@ -90,51 +64,47 @@ func (s *Site) Write(dstDir string) error {
if err != nil {
return err
}
data := s.Files[path]
data := s.Static[path]
if _, err := f.Write(data); err != nil {
return err
}
}
return nil
// Write the directory
return s.Directory.Write(dstDir)
}
// Manipulate processes and manipulates the site's content.
func (s *Site) Manipulate() error {
for path := range s.Files {
switch filepath.Ext(path) {
case ".gmi":
// Gather page data
content := string(s.Files[path])
page := NewPage(path, content)
builder := &strings.Builder{}
tmpl := s.Templates.Lookup("page.gmi")
if err := tmpl.Execute(builder, page); err != nil {
return err
}
page.Content = builder.String()
s.Pages[path] = page
s.Files[path] = []byte(page.Content)
}
}
for dir := range s.Dirs {
// Gather section data
section := NewSection(dir)
for _, path := range s.Dirs[dir] {
switch filepath.Ext(path) {
case ".gmi":
section.Pages = append(section.Pages, s.Pages[path])
}
}
dstPath := filepath.Join(dir, "index.gmi")
func (s *Site) Manipulate(dir *Directory) error {
// Write the directory index file, if it doesn't exist
if dir.Index == nil {
path := filepath.Join(dir.Path, "index.gmi")
builder := &strings.Builder{}
tmpl := s.Templates.Lookup("section.gmi")
if err := tmpl.Execute(builder, section); err != nil {
tmpl := s.Templates.Lookup("directory.gmi")
if err := tmpl.Execute(builder, dir); err != nil {
return err
}
s.Files[dstPath] = []byte(builder.String())
content := builder.String()
permalink := filepath.Dir(path)
if permalink == "." {
permalink = ""
}
page := &Page{
Path: path,
Permalink: "/" + permalink,
Content: content,
}
dir.Index = page
}
// Manipulate pages
for i := range dir.Pages {
builder := &strings.Builder{}
tmpl := s.Templates.Lookup("page.gmi")
if err := tmpl.Execute(builder, dir.Pages[i]); err != nil {
return err
}
dir.Pages[i].Content = builder.String()
}
return nil
@ -158,55 +128,158 @@ type Page struct {
var titleRE = regexp.MustCompile("^# ?([^#\r\n]+)")
func NewPage(path string, content string) *Page {
page := &Page{
Path: path,
Permalink: "/" + path,
Content: content,
}
// Try to parse the date from the page filename
var date time.Time
const layout = "2006-01-02"
base := filepath.Base(path)
if len(base) >= len(layout) {
dateStr := base[:len(layout)]
if date, err := time.Parse(layout, dateStr); err == nil {
page.Date = date
if time, err := time.Parse(layout, dateStr); err == nil {
date = time
}
// Remove the date from the path
base = base[len(layout):]
if len(base) > 0 {
// Remove a leading dash
if base[0] == '-' {
base = base[1:]
}
if len(base) > 0 {
dir := filepath.Dir(path)
if dir == "." {
dir = ""
}
path = filepath.Join(dir, base)
}
}
}
// Try to parse the title from the contents
var title string
if submatches := titleRE.FindStringSubmatch(content); submatches != nil {
page.Title = submatches[1]
title = submatches[1]
// Remove the title from the contents
content = content[len(submatches[0]):]
}
return page
return &Page{
Path: path,
Permalink: "/" + path,
Title: title,
Date: date,
Content: content,
}
}
// Section represents a section (i.e., a directory).
type Section struct {
// The path to this section.
// Directory represents a directory of pages.
type Directory struct {
// The path to this directory.
Path string
// The permalink to this section.
// The permalink to this directory.
Permalink string
// The pages in this section.
// The pages in this directory.
Pages []*Page
// The subdirectories of this directory.
Directories []*Directory
// The index file (index.gmi).
Index *Page
}
// NewSection returns a new Section with the given path.
func NewSection(path string) *Section {
func NewDirectory(path string) *Directory {
var permalink string
if path == "" {
permalink = "/"
} else {
permalink = "/" + path + "/"
}
return &Section{
return &Directory{
Path: path,
Permalink: permalink,
}
}
// Sort sorts pages by date.
func (s *Section) Sort() {
// TODO: Implement this
// Read reads from a directory and indexes the files and directories within it.
func (d *Directory) Read(site *Site, srcDir string, path string) error {
entries, err := ioutil.ReadDir(srcDir)
if err != nil {
return err
}
for _, entry := range entries {
name := entry.Name()
path := filepath.Join(path, name)
srcPath := filepath.Join(srcDir, path)
if entry.IsDir() {
// Gather directory data
dir := NewDirectory(path)
dir.Read(site, srcPath, path)
d.Directories = append(d.Directories, dir)
} else {
content, err := ioutil.ReadFile(srcPath)
if err != nil {
return err
}
switch filepath.Ext(name) {
case ".gmi", ".gemini":
// Gather page data
content := string(content)
page := NewPage(path, content)
if name == "index.gmi" {
d.Index = page
} else {
d.Pages = append(d.Pages, page)
}
default:
// Static file
site.Static[path] = content
}
}
}
return nil
}
// Write writes the Directory to the provided destination path.
func (d *Directory) Write(dstDir string) error {
// Create the directory
dirPath := filepath.Join(dstDir, d.Path)
if err := os.MkdirAll(dirPath, 0755); err != nil {
return err
}
// Write the files
for _, page := range d.Pages {
dstPath := filepath.Join(dstDir, page.Path)
f, err := os.Create(dstPath)
if err != nil {
return err
}
data := []byte(page.Content)
if _, err := f.Write(data); err != nil {
return err
}
}
// Write the index file
if d.Index != nil {
dstPath := filepath.Join(dstDir, d.Index.Path)
f, err := os.Create(dstPath)
if err != nil {
return err
}
data := []byte(d.Index.Content)
if _, err := f.Write(data); err != nil {
return err
}
}
// Write subdirectories
for _, dir := range d.Directories {
dir.Write(dstDir)
}
return nil
}

View file

@ -15,7 +15,7 @@ func run() error {
if err != nil {
return err
}
if err := site.Manipulate(); err != nil {
if err := site.Manipulate(site.Directory); err != nil {
return err
}
if err := site.Write("dst"); err != nil {