Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/inkssg.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ Supported fields:

- `name` — site name (available as `.Site.Name` in templates)
- `default_theme` — theme to use if not set in frontmatter
- `output_dir` — output directory (defaults to `public/`)
- `output_dir` — output directory (defaults to `public/`). Must be inside the project root.
- `pages_dir` — where pages live (defaults to `pages/`)

## ink.yaml vs frontmatter

Expand Down
63 changes: 50 additions & 13 deletions inkssg.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ func hasEmbeddedThemes() bool {
}

type Site struct {
Dir string
Pages []Page
Output string
Theme string
Config *SiteConfig
Dir string
Pages []Page
Output string
Theme string
PagesDir string
Config *SiteConfig
}

type Page struct {
Expand All @@ -51,6 +52,7 @@ type SiteConfig struct {
Install string `yaml:"install"`
DefaultTheme string `yaml:"default_theme"`
OutputDir string `yaml:"output_dir"`
PagesDir string `yaml:"pages_dir"`
Links []Link `yaml:"links"`
Projects []Project `yaml:"projects"`
Meta Meta `yaml:"meta"`
Expand Down Expand Up @@ -164,11 +166,12 @@ func Build(paths ...string) error {

func NewSite(dir string) (*Site, error) {
site := &Site{
Dir: dir,
Pages: []Page{},
Output: "public",
Theme: "minimal",
Config: &SiteConfig{},
Dir: dir,
Pages: []Page{},
Output: "public",
Theme: "minimal",
PagesDir: "pages",
Config: &SiteConfig{},
}

if err := site.loadConfig(); err != nil {
Expand Down Expand Up @@ -202,20 +205,24 @@ func (s *Site) loadConfig() error {
s.Theme = config.DefaultTheme
}

if config.PagesDir != "" {
s.PagesDir = config.PagesDir
}

s.Config = &config

return nil
}

func (s *Site) detect() error {
pagesDir := filepath.Join(s.Dir, "pages")
pagesDir := filepath.Join(s.Dir, s.PagesDir)
if _, err := os.Stat(pagesDir); os.IsNotExist(err) {
return fmt.Errorf("no pages/ folder found. Run 'inkssg new .' to scaffold a site.")
return fmt.Errorf("no %s/ folder found. Run 'inkssg new .' to scaffold a site.", s.PagesDir)
}

entries, err := os.ReadDir(pagesDir)
if err != nil {
return fmt.Errorf("cannot read pages/: %w", err)
return fmt.Errorf("cannot read %s/: %w", s.PagesDir, err)
}

for _, entry := range entries {
Expand Down Expand Up @@ -313,9 +320,39 @@ func (p *Page) renderMarkdown(data []byte, contentType string) string {
return buf.String()
}

func (s *Site) validateOutput() error {
if strings.TrimSpace(s.Output) == "" {
return fmt.Errorf("output_dir cannot be empty")
}

absDir, err := filepath.Abs(s.Dir)
if err != nil {
return fmt.Errorf("invalid project dir: %w", err)
}
absOut, err := filepath.Abs(filepath.Join(s.Dir, s.Output))
if err != nil {
return fmt.Errorf("invalid output_dir: %w", err)
}

if absOut == absDir {
return fmt.Errorf("output_dir cannot be the project root (would delete sources on rebuild)")
}

rel, err := filepath.Rel(absDir, absOut)
if err != nil || strings.HasPrefix(rel, "..") {
return fmt.Errorf("output_dir must be inside the project directory, got %q", s.Output)
}

return nil
}

func (s *Site) Build() error {
start := time.Now()

if err := s.validateOutput(); err != nil {
return err
}

if err := os.RemoveAll(filepath.Join(s.Dir, s.Output)); err != nil {
return fmt.Errorf("cannot clean output dir: %w", err)
}
Expand Down
Loading