Browse Source

Migration from github.

master
johnl 6 years ago
parent
commit
718cded516
2 changed files with 390 additions and 0 deletions
  1. +224
    -0
      main.go
  2. +166
    -0
      markdown.go

+ 224
- 0
main.go View File

@ -0,0 +1,224 @@
package main
import (
"fmt"
"html/template"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/johnnylee/goutil/fileutil"
"github.com/johnnylee/goutil/jsonutil"
"github.com/johnnylee/goutil/logutil"
)
type Config struct {
RootName string
ThumbWidth int
ThumbHeight int
ThumbQuality int
}
var imgExtensions = map[string]bool{
".jpg": true,
".png": true,
}
var log = logutil.New("jlstatic")
var tmpl = template.Must(template.ParseFiles("template.html"))
var conf Config
var srcDir string
var buildDir string
type Breadcrumb struct {
FullPath string
Name string
}
type Context struct {
Content template.HTML
Breadcrumbs []Breadcrumb
}
func NewContext(srcPath string) (Context, error) {
ctx := Context{}
relPath, err := filepath.Rel(srcDir, srcPath)
if err != nil {
return ctx, err
}
inputBuf, err := ioutil.ReadFile(srcPath)
if err != nil {
return ctx, err
}
ctx.Content = template.HTML(Markdown(inputBuf))
ctx.Breadcrumbs = []Breadcrumb{
Breadcrumb{"/", conf.RootName},
}
parents := strings.Split(filepath.Dir(relPath), "/")
path := "/"
for _, p := range parents {
if p == "." || len(p) == 0 {
continue
}
path = filepath.Join(path, p) + "/"
ctx.Breadcrumbs = append(ctx.Breadcrumbs, Breadcrumb{path, p})
}
return ctx, nil
}
func main() {
if err := jsonutil.Load(&conf, "config.json"); err != nil {
log.Err(err, "When reading config.json.")
return
}
srcDir, _ = filepath.Abs("src")
buildDir, _ = filepath.Abs("build")
// Walk files, generating content:
//
// * For index.md files, create a index.html file.
// * For images (jpg, png), copy the image and produce a thumbnail.
// * Any other files are simply coppied.
walk := func(srcPath string, info os.FileInfo, err error) error {
srcPath, err = filepath.Abs(srcPath)
if err != nil {
log.Err(err, "When getting absolute source path: %v", srcPath)
return err
}
// Skip directories.
if info.IsDir() {
log.Msg("Skipping directory: %v", srcPath)
return nil
}
log.Msg("Walking path: %v", srcPath)
// Get output directory and filename.
relPath, err := filepath.Rel(srcDir, srcPath)
if err != nil {
log.Err(err, "When walking path: %v", srcPath)
return err
}
dstPath := filepath.Join(buildDir, relPath)
outDir, outFile := filepath.Split(dstPath)
log.Msg(" Output path: %v", dstPath)
// Create the destination directory.
if err := os.MkdirAll(outDir, 0700); err != nil {
log.Err(err, "When creating output directory %v", outDir)
return err
}
// Is this an index.md file?
if outFile == "index.md" {
outFile = "index.html"
dstPath = filepath.Join(outDir, outFile)
ctx, err := NewContext(srcPath)
if err != nil {
log.Err(err, "When creating context for %v", srcPath)
return err
}
dstFile, err := os.Create(dstPath)
if err != nil {
log.Err(err, "When creating file %v", dstPath)
return err
}
defer dstFile.Close()
if err := tmpl.Execute(dstFile, ctx); err != nil {
log.Err(err, "When executing template")
return err
}
return nil
}
// Skip non-MD files if they exist.
_, isImage := imgExtensions[filepath.Ext(outFile)]
if isImage && fileutil.FileExists(dstPath) {
log.Msg(" Skipping existing image.")
return nil
}
// Copy all other files.
if err := copyFile(srcPath, dstPath); err != nil {
log.Err(err, "When copying file: %v", srcPath)
return err
}
// If this isn't an image, we're done.
if !isImage {
return nil
}
// Create the thumbnail directory.
thumbDir := filepath.Join(outDir, "t")
log.Msg(" Creating thumbnail directory: %v", thumbDir)
if err := os.MkdirAll(thumbDir, 0700); err != nil {
log.Err(err, "When creating thumbnail directory")
return err
}
thumbFile := filepath.Join(thumbDir, outFile)
log.Msg(" Creating thumbnail: %v", thumbFile)
// Use imagemagick to create the thumbnail.
shellCmd := fmt.Sprintf("convert "+
"-strip "+
"-interlace Plane "+
"-quality %v%% "+
"-thumbnail %vx%v %v %v",
conf.ThumbQuality, conf.ThumbWidth, conf.ThumbHeight,
dstPath, thumbFile)
cmd := exec.Command("bash", "-c", shellCmd)
if err := cmd.Run(); err != nil {
log.Err(err, "When creating image thumbnail for %v", dstPath)
return err
}
return nil
}
if err := filepath.Walk("src", walk); err != nil {
log.Err(err, "Walk failed.")
}
}
func processMarkdown(relPath, srcPath, dstPath string) error {
return nil
}
func copyFile(srcPath, dstPath string) error {
srcFile, err := os.Open(srcPath)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(dstPath)
if err != nil {
return err
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
return err
}

+ 166
- 0
markdown.go View File

@ -0,0 +1,166 @@
package main
import (
"bytes"
"os/exec"
"strings"
"github.com/russross/blackfriday"
)
const commonHtmlFlags = 0 |
blackfriday.HTML_USE_SMARTYPANTS |
blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
blackfriday.HTML_FOOTNOTE_RETURN_LINKS
const commonExtensions = 0 |
blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
blackfriday.EXTENSION_TABLES |
blackfriday.EXTENSION_FENCED_CODE |
blackfriday.EXTENSION_AUTOLINK |
blackfriday.EXTENSION_STRIKETHROUGH |
blackfriday.EXTENSION_SPACE_HEADERS |
blackfriday.EXTENSION_HEADER_IDS |
blackfriday.EXTENSION_FOOTNOTES |
blackfriday.EXTENSION_DEFINITION_LISTS
type Renderer struct {
*blackfriday.Html
}
var renderer = &Renderer{
Html: blackfriday.HtmlRenderer(
commonHtmlFlags, "", "").(*blackfriday.Html),
}
// BlockCode: Render code blocks using `pygmentize` syntax highlighter.
func (r *Renderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
// If the language isn't specified, fallback on the standard processor.
if len(lang) == 0 {
r.Html.BlockCode(out, text, lang)
return
}
var stderr bytes.Buffer
cmd := exec.Command("pygmentize", "-l"+lang, "-fhtml")
cmd.Stdin = bytes.NewReader(text)
cmd.Stdout = out
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
log.Err(err, "When running pygmentize")
}
}
// Coppied from BlackFriday for custom Image renderer:
func escapeSingleChar(char byte) (string, bool) {
if char == '"' {
return """, true
}
if char == '&' {
return "&", true
}
if char == '<' {
return "&lt;", true
}
if char == '>' {
return "&gt;", true
}
return "", false
}
// Coppied from BlackFriday for custom Image renderer:
func attrEscape(out *bytes.Buffer, src []byte) {
org := 0
for i, ch := range src {
if entity, ok := escapeSingleChar(ch); ok {
if i > org {
// copy all the normal characters since the last escape
out.Write(src[org:i])
}
org = i + 1
out.WriteString(entity)
}
}
if org < len(src) {
out.Write(src[org:])
}
}
func (r *Renderer) Image(out *bytes.Buffer, link, title, alt []byte) {
parts := strings.Split(string(link), " =")
if len(parts) == 0 {
return
}
class := ""
style := ""
if len(parts) == 2 {
link = []byte(strings.TrimSpace(parts[0]))
size := strings.TrimSpace(parts[1])
if len(size) > 0 {
float := size[len(size)-1]
if float == 'l' {
style = `float:left;`
size = size[:len(size)-1]
} else if float == 'r' {
style = `float:right;`
size = size[:len(size)-1]
}
class = "img-" + strings.TrimSpace(size)
}
}
out.WriteString(`<img src="`)
attrEscape(out, link)
out.WriteString(`" `)
out.WriteString(`alt="`)
if len(alt) > 0 {
attrEscape(out, alt)
}
out.WriteString(`" `)
if len(title) > 0 {
out.WriteString(`title="`)
attrEscape(out, title)
out.WriteString(`" `)
}
if len(class) > 0 {
out.WriteString(`class="`)
attrEscape(out, []byte(class))
out.WriteString(`" `)
}
if len(style) > 0 {
out.WriteString(`style="`)
attrEscape(out, []byte(style))
out.WriteString(`" `)
}
out.WriteString(">")
}
// In the pre-rendering step, we implement our custom clearfix syntax.
func preRender(input []byte) []byte {
sInput := string(input)
lines := strings.Split(sInput, "\n")
for i, line := range lines {
line = strings.Trim(line, "\r") // Carriage returns!
if line == `<-->` {
lines[i] = `<div class="clearfix"></div>`
}
}
return []byte(strings.Join(lines, "\n"))
}
func Markdown(input []byte) []byte {
input = preRender(input)
return blackfriday.Markdown(input, renderer, commonExtensions)
}

Loading…
Cancel
Save