main.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package main
  2. import (
  3. "html/template"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "runtime"
  8. "runtime/pprof"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/cdelorme/go-log"
  13. "github.com/cdelorme/go-maps"
  14. "github.com/cdelorme/go-option"
  15. )
  16. // check that a path exists
  17. // does not care if it is a directory
  18. // will not say whether user has rw access, but
  19. // will throw an error if the user cannot read the parent directory
  20. func exists(path string) (bool, error) {
  21. _, err := os.Stat(path)
  22. if err == nil {
  23. return true, nil
  24. }
  25. if os.IsNotExist(err) {
  26. return false, nil
  27. }
  28. return false, err
  29. }
  30. // if within a git repo, gets git version as a short-hash
  31. // otherwise falls back to a unix timestamp
  32. func version() string {
  33. version := strconv.FormatInt(time.Now().Unix(), 10)
  34. out, err := exec.Command("sh", "-c", "git rev-parse --short HEAD").Output()
  35. if err == nil {
  36. version = strings.Trim(string(out), "\n")
  37. }
  38. return version
  39. }
  40. // remove the extension from a given filename
  41. func basename(name string) string {
  42. return filepath.Base(strings.TrimSuffix(name, filepath.Ext(name)))
  43. }
  44. func main() {
  45. // get current directory
  46. cwd, _ := os.Getwd()
  47. // prepare staticmd with dependencies & defaults
  48. staticmd := Staticmd{
  49. Logger: log.Logger{Level: log.Error},
  50. Subdirectories: make(map[string][]string),
  51. Indexes: make(map[string][]string),
  52. Version: version(),
  53. Input: cwd,
  54. Output: filepath.Join(cwd, "public/"),
  55. }
  56. // optimize concurrent processing
  57. staticmd.MaxParallelism = runtime.NumCPU()
  58. runtime.GOMAXPROCS(staticmd.MaxParallelism)
  59. // prepare cli options
  60. appOptions := option.App{Description: "command line tool for generating deliverable static content"}
  61. appOptions.Flag("template", "path to the template file", "--template", "-t")
  62. appOptions.Flag("input", "path to the markdown files", "--input", "-i")
  63. appOptions.Flag("output", "path to place generated content", "--output", "-o")
  64. appOptions.Flag("book", "combine all content into a single file", "--book", "-b")
  65. appOptions.Flag("relative", "use relative paths instead of absolute paths", "--relative", "-r")
  66. appOptions.Flag("debug", "verbose debug output", "--debug", "-d")
  67. appOptions.Flag("profile", "produce profile output to supplied path", "--profile", "-p")
  68. appOptions.Example("-t template.tmpl -i . -b")
  69. appOptions.Example("-t template.tmpl -i src/ -o out/ -r")
  70. flags := appOptions.Parse()
  71. // apply flags
  72. t, _ := maps.String(&flags, "", "template")
  73. if tmpl, err := template.ParseFiles(t); err != nil {
  74. staticmd.Logger.Error("Failed to open template: %s", err)
  75. } else {
  76. staticmd.Template = *tmpl
  77. }
  78. staticmd.Input, _ = maps.String(&flags, staticmd.Input, "input")
  79. staticmd.Output, _ = maps.String(&flags, staticmd.Output, "output")
  80. staticmd.Book, _ = maps.Bool(&flags, staticmd.Book, "book")
  81. staticmd.Relative, _ = maps.Bool(&flags, staticmd.Relative, "relative")
  82. // sanitize input & output
  83. staticmd.Input, _ = filepath.Abs(staticmd.Input)
  84. staticmd.Output, _ = filepath.Abs(staticmd.Output)
  85. // optionally enable debugging
  86. if debug, _ := maps.Bool(&flags, false, "debug"); debug {
  87. staticmd.Logger.Level = log.Debug
  88. }
  89. // optionally enable profiling
  90. if profile, _ := maps.String(&flags, "", "profile"); profile != "" {
  91. f, _ := os.Create(profile)
  92. pprof.StartCPUProfile(f)
  93. defer pprof.StopCPUProfile()
  94. }
  95. // sanitize & validate properties
  96. staticmd.Input = filepath.Clean(staticmd.Input)
  97. staticmd.Output = filepath.Clean(staticmd.Output)
  98. // print debug status
  99. staticmd.Logger.Debug("Staticmd State: %+v", staticmd)
  100. if err := filepath.Walk(staticmd.Input, staticmd.Walk); err != nil {
  101. staticmd.Logger.Error("failed to walk directory: %s", err)
  102. }
  103. staticmd.Logger.Debug("Pages: %+v", staticmd.Pages)
  104. // build
  105. if staticmd.Book {
  106. staticmd.Single()
  107. } else {
  108. staticmd.Multi()
  109. }
  110. }