2024-02-10 15:37:38 +01:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-02-12 19:16:56 +01:00
|
|
|
"github.com/facundoolano/blorg/templates"
|
2024-02-11 17:16:10 +01:00
|
|
|
"io"
|
2024-02-10 15:37:38 +01:00
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-02-11 17:16:10 +01:00
|
|
|
"strings"
|
2024-02-10 15:37:38 +01:00
|
|
|
)
|
|
|
|
|
2024-02-12 19:16:56 +01:00
|
|
|
const SRC_DIR = "src"
|
|
|
|
const TARGET_DIR = "target"
|
|
|
|
const LAYOUT_DIR = "layouts"
|
|
|
|
const FILE_RW_MODE = 0777
|
|
|
|
|
2024-02-10 15:37:38 +01:00
|
|
|
func Init() error {
|
|
|
|
// get working directory
|
|
|
|
// default to .
|
|
|
|
// if not exist, create directory
|
|
|
|
// copy over default files
|
|
|
|
fmt.Println("not implemented yet")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the files in src/ render them and copy the result to target/
|
2024-02-10 15:45:03 +01:00
|
|
|
// FIXME pass src and target by arg
|
2024-02-10 15:37:38 +01:00
|
|
|
func Build() error {
|
2024-02-12 19:16:56 +01:00
|
|
|
_, err := os.ReadDir(SRC_DIR)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return fmt.Errorf("missing %s directory", SRC_DIR)
|
|
|
|
} else if err != nil {
|
|
|
|
return fmt.Errorf("couldn't read %s", SRC_DIR)
|
|
|
|
}
|
|
|
|
|
|
|
|
site := Site{
|
2024-02-12 19:38:26 +01:00
|
|
|
layouts: make(map[string]templates.Template),
|
|
|
|
templateIndex: make(map[string]*templates.Template),
|
2024-02-12 19:16:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME these sound like they should be site methods too
|
|
|
|
PHASES := []func(*Site) error{
|
|
|
|
loadConfig,
|
|
|
|
loadLayouts,
|
|
|
|
loadTemplates,
|
|
|
|
writeTarget,
|
|
|
|
}
|
|
|
|
for _, phaseFun := range PHASES {
|
|
|
|
if err := phaseFun(&site); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfig(site *Site) error {
|
|
|
|
// context["config"]
|
|
|
|
return nil
|
|
|
|
}
|
2024-02-10 15:37:38 +01:00
|
|
|
|
2024-02-12 19:16:56 +01:00
|
|
|
func loadLayouts(site *Site) error {
|
|
|
|
files, err := os.ReadDir(LAYOUT_DIR)
|
2024-02-10 15:37:38 +01:00
|
|
|
if os.IsNotExist(err) {
|
2024-02-12 19:16:56 +01:00
|
|
|
return nil
|
2024-02-10 15:37:38 +01:00
|
|
|
} else if err != nil {
|
2024-02-12 19:16:56 +01:00
|
|
|
return err
|
2024-02-10 15:37:38 +01:00
|
|
|
}
|
|
|
|
|
2024-02-12 19:16:56 +01:00
|
|
|
for _, entry := range files {
|
|
|
|
if !entry.IsDir() {
|
|
|
|
filename := entry.Name()
|
|
|
|
path := filepath.Join(LAYOUT_DIR, filename)
|
|
|
|
templ, err := templates.Parse(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-02-10 15:37:38 +01:00
|
|
|
|
2024-02-12 19:16:56 +01:00
|
|
|
layout_name := strings.TrimSuffix(filename, filepath.Ext(filename))
|
|
|
|
site.layouts[layout_name] = *templ
|
|
|
|
}
|
|
|
|
}
|
2024-02-11 17:16:10 +01:00
|
|
|
|
2024-02-12 19:16:56 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadTemplates(site *Site) error {
|
|
|
|
return filepath.WalkDir(SRC_DIR, func(path string, entry fs.DirEntry, err error) error {
|
|
|
|
if !entry.IsDir() {
|
|
|
|
templ, err := templates.Parse(path)
|
2024-02-12 19:33:30 +01:00
|
|
|
// if sometime fails or this is not a template, skip
|
|
|
|
if err != nil || templ == nil {
|
2024-02-11 17:16:10 +01:00
|
|
|
return err
|
2024-02-10 15:37:38 +01:00
|
|
|
}
|
|
|
|
|
2024-02-12 19:33:30 +01:00
|
|
|
// posts are templates that can be chronologically sorted --that have a date.
|
|
|
|
// the rest are pages.
|
|
|
|
if _, ok := templ.Metadata["date"]; ok {
|
2024-02-12 19:16:56 +01:00
|
|
|
site.posts = append(site.posts, *templ)
|
2024-02-12 19:33:30 +01:00
|
|
|
} else {
|
2024-02-12 19:16:56 +01:00
|
|
|
site.pages = append(site.pages, *templ)
|
|
|
|
}
|
2024-02-12 19:38:26 +01:00
|
|
|
site.templateIndex[path] = templ
|
|
|
|
|
|
|
|
// TODO load tags
|
2024-02-12 19:16:56 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeTarget(site *Site) error {
|
|
|
|
// clear previous target contents
|
|
|
|
os.RemoveAll(TARGET_DIR)
|
|
|
|
os.Mkdir(TARGET_DIR, FILE_RW_MODE)
|
|
|
|
|
|
|
|
// walk the source directory, creating directories and files at the target dir
|
|
|
|
return filepath.WalkDir(SRC_DIR, func(path string, entry fs.DirEntry, err error) error {
|
|
|
|
subpath, _ := filepath.Rel(SRC_DIR, path)
|
|
|
|
targetPath := filepath.Join(TARGET_DIR, subpath)
|
2024-02-11 17:16:10 +01:00
|
|
|
|
2024-02-12 19:16:56 +01:00
|
|
|
if entry.IsDir() {
|
|
|
|
os.MkdirAll(targetPath, FILE_RW_MODE)
|
|
|
|
} else {
|
|
|
|
|
2024-02-12 19:38:26 +01:00
|
|
|
if templ, ok := site.templateIndex[path]; ok {
|
2024-02-12 19:16:56 +01:00
|
|
|
// if a template was found at source, render it
|
|
|
|
content, err := site.render(templ)
|
2024-02-11 17:16:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the file contents over to target at the same location
|
2024-02-12 19:16:56 +01:00
|
|
|
targetPath = strings.TrimSuffix(targetPath, filepath.Ext(targetPath)) + templ.Ext()
|
2024-02-11 17:16:10 +01:00
|
|
|
fmt.Println("writing ", targetPath)
|
2024-02-12 19:16:56 +01:00
|
|
|
return os.WriteFile(targetPath, []byte(content), FILE_RW_MODE)
|
2024-02-11 17:16:10 +01:00
|
|
|
} else {
|
|
|
|
// if a non template was found, copy file as is
|
|
|
|
fmt.Println("writing ", targetPath)
|
|
|
|
return copyFile(path, targetPath)
|
2024-02-10 15:37:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-11 17:16:10 +01:00
|
|
|
func copyFile(source string, target string) error {
|
|
|
|
// does this need to be so verbose?
|
|
|
|
srcFile, err := os.Open(source)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer srcFile.Close()
|
|
|
|
|
|
|
|
targetFile, _ := os.Create(target)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer targetFile.Close()
|
|
|
|
|
|
|
|
_, err = io.Copy(targetFile, srcFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetFile.Sync()
|
|
|
|
}
|
|
|
|
|
2024-02-10 15:37:38 +01:00
|
|
|
func New() error {
|
|
|
|
// prompt for title
|
|
|
|
// slugify
|
|
|
|
// fail if file already exist
|
|
|
|
// create a new .org file with the slug
|
|
|
|
// add front matter and org options
|
|
|
|
fmt.Println("not implemented yet")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Serve() error {
|
|
|
|
// build
|
|
|
|
// serve target with file server
|
|
|
|
// (later watch and live reload)
|
|
|
|
fmt.Println("not implemented yet")
|
|
|
|
return nil
|
|
|
|
}
|