mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-12-31 23:54:55 +00: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:
|
A kiln site is organized in the following way:
|
||||||
|
|
||||||
```
|
```
|
||||||
src/ Site source
|
src/ Site source
|
||||||
templates/ Templates
|
templates/ Templates
|
||||||
page.gmi Page template
|
page.gmi Page template
|
||||||
section.gmi Section template
|
directory.gmi Directory template
|
||||||
dst/ Site destination
|
dst/ Site destination
|
||||||
```
|
```
|
||||||
|
|
||||||
Running `kiln` takes the contents in `src`, runs them through the templates in
|
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
|
- `Permalink`: Permalink to the page
|
||||||
- `Content`: The contents of the file (including the title)
|
- `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
|
- `Path`: Relative path to the directory
|
||||||
- `Permalink`: Permalink to the section
|
- `Permalink`: Permalink to the directory
|
||||||
- `Pages`: The pages in this section
|
- `Pages`: The pages in this directory
|
||||||
|
- `Directories`: The subdirectories of this directory
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
|
@ -61,4 +62,4 @@ Templates are located in the `templates` directory.
|
||||||
There are currently two supported templates:
|
There are currently two supported templates:
|
||||||
|
|
||||||
- `page.gmi`: The template used for pages
|
- `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 {
|
type Site struct {
|
||||||
Files map[string][]byte
|
Static map[string][]byte // Static files
|
||||||
Dirs map[string][]string
|
Directory *Directory // Site directory
|
||||||
Pages map[string]*Page
|
Templates *template.Template // Templates
|
||||||
Templates *template.Template
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadSite loads and returns a Site.
|
||||||
|
// It reads site content from the specified source directory.
|
||||||
func LoadSite(srcDir string) (*Site, error) {
|
func LoadSite(srcDir string) (*Site, error) {
|
||||||
tmpl, err := template.New("templates").ParseGlob("templates/*.gmi")
|
tmpl, err := template.New("templates").ParseGlob("templates/*.gmi")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -24,58 +25,31 @@ func LoadSite(srcDir string) (*Site, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
site := &Site{
|
site := &Site{
|
||||||
Files: map[string][]byte{},
|
Static: map[string][]byte{},
|
||||||
Dirs: map[string][]string{},
|
Directory: NewDirectory(""),
|
||||||
Pages: map[string]*Page{},
|
|
||||||
Templates: tmpl,
|
Templates: tmpl,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := site.ReadDir(srcDir, ""); err != nil {
|
if err := site.Directory.Read(site, srcDir, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return site, nil
|
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.
|
// Write writes the contents of the Index to the provided destination directory.
|
||||||
func (s *Site) Write(dstDir string) error {
|
func (s *Site) Write(dstDir string) error {
|
||||||
// Empty the destination directory
|
// Empty the destination directory
|
||||||
if err := os.RemoveAll(dstDir); err != nil {
|
if err := os.RemoveAll(dstDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Create the destination directory
|
||||||
if err := os.MkdirAll(dstDir, 0755); err != nil {
|
if err := os.MkdirAll(dstDir, 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for path := range s.Files {
|
// Write the static files
|
||||||
|
for path := range s.Static {
|
||||||
// Create any parent directories
|
// Create any parent directories
|
||||||
if dir := filepath.Dir(path); dir != "." {
|
if dir := filepath.Dir(path); dir != "." {
|
||||||
dstPath := filepath.Join(dstDir, dir)
|
dstPath := filepath.Join(dstDir, dir)
|
||||||
|
@ -90,51 +64,47 @@ func (s *Site) Write(dstDir string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data := s.Files[path]
|
data := s.Static[path]
|
||||||
if _, err := f.Write(data); err != nil {
|
if _, err := f.Write(data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
// Write the directory
|
||||||
|
return s.Directory.Write(dstDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manipulate processes and manipulates the site's content.
|
// Manipulate processes and manipulates the site's content.
|
||||||
func (s *Site) Manipulate() error {
|
func (s *Site) Manipulate(dir *Directory) error {
|
||||||
for path := range s.Files {
|
// Write the directory index file, if it doesn't exist
|
||||||
switch filepath.Ext(path) {
|
if dir.Index == nil {
|
||||||
case ".gmi":
|
path := filepath.Join(dir.Path, "index.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")
|
|
||||||
builder := &strings.Builder{}
|
builder := &strings.Builder{}
|
||||||
tmpl := s.Templates.Lookup("section.gmi")
|
tmpl := s.Templates.Lookup("directory.gmi")
|
||||||
if err := tmpl.Execute(builder, section); err != nil {
|
if err := tmpl.Execute(builder, dir); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
|
@ -158,55 +128,158 @@ type Page struct {
|
||||||
var titleRE = regexp.MustCompile("^# ?([^#\r\n]+)")
|
var titleRE = regexp.MustCompile("^# ?([^#\r\n]+)")
|
||||||
|
|
||||||
func NewPage(path string, content string) *Page {
|
func NewPage(path string, content string) *Page {
|
||||||
page := &Page{
|
|
||||||
Path: path,
|
|
||||||
Permalink: "/" + path,
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the date from the page filename
|
// Try to parse the date from the page filename
|
||||||
|
var date time.Time
|
||||||
const layout = "2006-01-02"
|
const layout = "2006-01-02"
|
||||||
base := filepath.Base(path)
|
base := filepath.Base(path)
|
||||||
if len(base) >= len(layout) {
|
if len(base) >= len(layout) {
|
||||||
dateStr := base[:len(layout)]
|
dateStr := base[:len(layout)]
|
||||||
if date, err := time.Parse(layout, dateStr); err == nil {
|
if time, err := time.Parse(layout, dateStr); err == nil {
|
||||||
page.Date = date
|
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
|
// Try to parse the title from the contents
|
||||||
|
var title string
|
||||||
if submatches := titleRE.FindStringSubmatch(content); submatches != nil {
|
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).
|
// Directory represents a directory of pages.
|
||||||
type Section struct {
|
type Directory struct {
|
||||||
// The path to this section.
|
// The path to this directory.
|
||||||
Path string
|
Path string
|
||||||
// The permalink to this section.
|
// The permalink to this directory.
|
||||||
Permalink string
|
Permalink string
|
||||||
// The pages in this section.
|
// The pages in this directory.
|
||||||
Pages []*Page
|
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.
|
// NewSection returns a new Section with the given path.
|
||||||
func NewSection(path string) *Section {
|
func NewDirectory(path string) *Directory {
|
||||||
var permalink string
|
var permalink string
|
||||||
if path == "" {
|
if path == "" {
|
||||||
permalink = "/"
|
permalink = "/"
|
||||||
} else {
|
} else {
|
||||||
permalink = "/" + path + "/"
|
permalink = "/" + path + "/"
|
||||||
}
|
}
|
||||||
return &Section{
|
return &Directory{
|
||||||
Path: path,
|
Path: path,
|
||||||
Permalink: permalink,
|
Permalink: permalink,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sorts pages by date.
|
// Read reads from a directory and indexes the files and directories within it.
|
||||||
func (s *Section) Sort() {
|
func (d *Directory) Read(site *Site, srcDir string, path string) error {
|
||||||
// TODO: Implement this
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := site.Manipulate(); err != nil {
|
if err := site.Manipulate(site.Directory); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := site.Write("dst"); err != nil {
|
if err := site.Write("dst"); err != nil {
|
||||||
|
|
Loading…
Reference in a new issue