mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-11-23 08:52:18 +01:00
Remove titles and dates from pages
This commit is contained in:
parent
d0713476c0
commit
e00ca65aee
23
README.md
23
README.md
@ -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
257
kiln.go
@ -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
|
||||
}
|
||||
|
2
main.go
2
main.go
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user