1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
// Copyright (c) 2023 Nicolas Paul All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package crocc is a simple Markdown-based static site generator.
package main /* import "go.nc0.fr/crocc" */
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"text/template"
)
var (
out = flag.String("out", "dst", "output directory")
url = flag.String("url", "http://localhost", "site URL")
generateHidden = flag.Bool("hidden", false, "generate hidden pages")
printVersion = flag.Bool("version", false, "print version and exit")
)
const usage string = `crocc is a simple Markdown-based static site generator.
It is designed to be simple and fast, and to be used with a static web server.
Author: Nicolas Paul <n@nc0.fr> (https://nc0.fr)
License: BSD 3-Clause
Repository: https://github.com/n1c00o/crocc
Usage:
crocc [options] [input directory]
Options:`
// Set at compilation time
var (
version string
date string
)
var (
in string
htmlTemplate template.Template
)
func init() {
flag.Usage = func() {
fmt.Println(usage)
flag.PrintDefaults()
}
}
// versionFormat returns a string containing the build information.
func versionFormat() string {
return fmt.Sprintf("crocc version %s %s/%s %s date %s",
version, runtime.GOOS, runtime.GOARCH, runtime.Compiler, date)
}
func main() {
flag.Parse()
log.SetFlags(0)
if *printVersion {
log.Print(versionFormat())
return
}
// Check input directory
in = flag.Arg(0)
if in == "" {
log.Fatalln("no input directory specified")
}
if _, err := os.Stat(in); os.IsNotExist(err) {
log.Fatalf("input directory %q does not exist", in)
}
// Check output directory
if _, err := os.Stat(*out); !os.IsNotExist(err) {
log.Fatalf("output directory %q already exists", *out)
} else {
if err := os.MkdirAll(*out, 0755); err != nil {
log.Fatalf("unable to create output directory %q: %v", *out, err)
}
}
// Retrieve template file
templatePath := filepath.Join(in, ".crocc.html")
if _, err := os.Stat(templatePath); os.IsNotExist(err) {
log.Fatalf("template file %q does not exist", templatePath)
}
tp, err := os.ReadFile(templatePath)
if err != nil {
log.Fatalf("error reading template file %q: %v", templatePath, err)
}
htmlTemplate = *template.Must(template.New("html-template").Parse(string(tp)))
// Logic
if err := filepath.WalkDir(in, Crocc); err != nil {
log.Fatalf("unable to complete generation from %q: %v", in, err)
}
}
// Crocc is the function that applies to every file in a directory.
// child corresponds to the path of a nested subdirectory, relative to the root.
// For example, if the root is "src" and the child is "foo/bar", the function
// will be applied to "src/foo/bar".
func Crocc(path string, d os.DirEntry, e error) error {
if e != nil {
return e
}
// ignore the input directory itself and hidden files
if path == in || strings.HasPrefix(d.Name(), ".") {
return nil
}
o := strings.Replace(path, in, *out, 1)
// If the file is a directory, create it in the output directory
if d.IsDir() {
return TransformDirectory(o)
}
// Copy non-Markdown files into the output directory
if filepath.Ext(path) != ".md" &&
filepath.Ext(path) != ".markdown" &&
filepath.Ext(path) != ".mdown" &&
filepath.Ext(path) != ".Markdown" {
return TransformNonMarkdownFile(path, o)
}
// If the file is a Markdown file, transform it into HTML
o = strings.TrimSuffix(o, filepath.Ext(o)) + ".html"
s := strings.TrimSuffix(strings.TrimPrefix(o, *out), ".html")
if err := TransformMarkdownFile(path, o, s); err != nil {
return err
}
return nil
}
|