ref: 7b960ac121fa8cdbd9b52c4f2adf66d39440758e
parent: e02dc6742af9d1a23bdeaa89704fdfcdd075c875
author: spf13 <[email protected]>
date: Mon Nov 3 19:39:37 EST 2014
New targets & new renderers and write methods [WIP]
--- a/hugolib/planner.go
+++ b/hugolib/planner.go
@@ -24,23 +24,23 @@
}
fmt.Fprintf(out, "\n")
fmt.Fprintf(out, " canonical => ")
- if s.Target == nil {
+ if s.Targets.Page == nil {
fmt.Fprintf(out, "%s\n\n", "!no target specified!")
continue
}
- trns, err := s.Target.Translate(p.TargetPath())
+ trns, err := s.PageTarget().Translate(p.TargetPath())
if err != nil {
return err
}
fmt.Fprintf(out, "%s\n", trns)
- if s.Alias == nil {
+ if s.Targets.Alias == nil {
continue
}
for _, alias := range p.Aliases {
- aliasTrans, err := s.Alias.Translate(alias)
+ aliasTrans, err := s.AliasTarget().Translate(alias)
if err != nil {
return err
}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -69,8 +69,7 @@
Shortcodes map[string]ShortcodeFunc
Menus Menus
timer *nitro.B
- Target target.Output
- Alias target.AliasPublisher
+ Targets targetList
Completed chan bool
RunMode runmode
params map[string]interface{}
@@ -78,6 +77,12 @@
futureCount int
}
+type targetList struct {
+ Page target.Output
+ File target.Output
+ Alias target.AliasPublisher
+}
+
type SiteInfo struct {
BaseUrl template.URL
Taxonomies TaxonomyList
@@ -157,10 +162,6 @@
func (s *Site) Analyze() {
s.Process()
- s.initTarget()
- s.Alias = &target.HTMLRedirectAlias{
- PublishDir: s.absPublishDir(),
- }
s.ShowPlan(os.Stdout)
}
@@ -671,7 +672,7 @@
if err != nil {
return err
}
- if err := s.WriteAlias(a, template.HTML(plink)); err != nil {
+ if err := s.WriteDestAlias(a, template.HTML(plink)); err != nil {
return err
}
}
@@ -733,7 +734,12 @@
layouts = append(layouts, "_default/single.html")
}
- results <- s.render("page "+p.FullFilePath(), p, p.TargetPath(), s.appendThemeTemplates(layouts)...)
+ b, err := s.renderPage("page "+p.FullFilePath(), p, s.appendThemeTemplates(layouts)...)
+ if err != nil {
+ results <- err
+ } else {
+ results <- s.WriteDestPage(p.TargetPath(), b)
+ }
}
}
@@ -843,10 +849,15 @@
for t := range taxes {
n, base := s.newTaxonomyNode(t)
layouts := []string{"taxonomy/" + t.singular + ".html", "indexes/" + t.singular + ".html", "_default/taxonomy.html", "_default/list.html"}
- err := s.render("taxononomy "+t.singular, n, base+".html", s.appendThemeTemplates(layouts)...)
+ b, err := s.renderPage("taxononomy "+t.singular, n, s.appendThemeTemplates(layouts)...)
if err != nil {
results <- err
continue
+ } else {
+ err := s.WriteDestPage(base+".html", b)
+ if err != nil {
+ results <- err
+ }
}
if !viper.GetBool("DisableRSS") {
@@ -853,13 +864,17 @@
// XML Feed
s.setUrls(n, base+".xml")
rssLayouts := []string{"taxonomy/" + t.singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
- err := s.render("taxonomy "+t.singular+" rss", n, base+".xml", s.appendThemeTemplates(rssLayouts)...)
+ b, err := s.renderXML("taxonomy "+t.singular+" rss", n, s.appendThemeTemplates(rssLayouts)...)
if err != nil {
results <- err
continue
+ } else {
+ err := s.WriteDestFile(base+".xml", b)
+ if err != nil {
+ results <- err
+ }
}
}
- results <- nil
}
}
@@ -879,10 +894,13 @@
layouts := []string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"}
layouts = s.appendThemeTemplates(layouts)
if s.layoutExists(layouts...) {
- err := s.render("taxonomy terms for "+singular, n, plural+"/index.html", layouts...)
+ b, err := s.renderPage("taxonomy terms for "+singular, n, layouts...)
if err != nil {
return err
}
+ if err := s.WriteDestPage(plural+"/index.html", b); err != nil {
+ return err
+ }
}
}
@@ -903,19 +921,25 @@
n.Data["Pages"] = data.Pages()
layouts := []string{"section/" + section + ".html", "_default/section.html", "_default/list.html", "indexes/" + section + ".html", "_default/indexes.html"}
- err := s.render("section "+section, n, section, s.appendThemeTemplates(layouts)...)
+ b, err := s.renderPage("section "+section, n, s.appendThemeTemplates(layouts)...)
if err != nil {
return err
}
+ if err := s.WriteDestPage(section, b); err != nil {
+ return err
+ }
if !viper.GetBool("DisableRSS") {
// XML Feed
rssLayouts := []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
s.setUrls(n, section+".xml")
- err = s.render("section "+section+" rss", n, section+".xml", s.appendThemeTemplates(rssLayouts)...)
+ b, err = s.renderXML("section "+section+" rss", n, s.appendThemeTemplates(rssLayouts)...)
if err != nil {
return err
}
+ if err := s.WriteDestFile(section+".xml", b); err != nil {
+ return err
+ }
}
}
return nil
@@ -932,10 +956,13 @@
func (s *Site) RenderHomePage() error {
n := s.newHomeNode()
layouts := []string{"index.html", "_default/list.html", "_default/single.html"}
- err := s.render("homepage", n, "/", s.appendThemeTemplates(layouts)...)
+ b, err := s.renderPage("homepage", n, s.appendThemeTemplates(layouts)...)
if err != nil {
return err
}
+ if err := s.WriteDestPage("/", b); err != nil {
+ return err
+ }
if !viper.GetBool("DisableRSS") {
// XML Feed
@@ -953,31 +980,28 @@
if !viper.GetBool("DisableRSS") {
rssLayouts := []string{"rss.xml", "_default/rss.xml", "_internal/_default/rss.xml"}
- err := s.render("homepage rss", n, ".xml", s.appendThemeTemplates(rssLayouts)...)
+ b, err := s.renderXML("homepage rss", n, s.appendThemeTemplates(rssLayouts)...)
if err != nil {
return err
}
+ if err := s.WriteDestFile("rss.xml", b); err != nil {
+ return err
+ }
}
}
- // Force `UglyUrls` option to force `404.html` file name
- switch s.Target.(type) {
- case *target.Filesystem:
- if !s.Target.(*target.Filesystem).UglyUrls {
- s.Target.(*target.Filesystem).UglyUrls = true
- defer func() { s.Target.(*target.Filesystem).UglyUrls = false }()
- }
- }
-
n.Url = helpers.Urlize("404.html")
n.Title = "404 Page not found"
n.Permalink = s.permalink("404.html")
nfLayouts := []string{"404.html"}
- nfErr := s.render("404 page", n, "404.html", s.appendThemeTemplates(nfLayouts)...)
+ b, nfErr := s.renderPage("404 page", n, s.appendThemeTemplates(nfLayouts)...)
if nfErr != nil {
return nfErr
}
+ if err := s.WriteDestFile("404.html", b); err != nil {
+ return err
+ }
return nil
}
@@ -1017,20 +1041,23 @@
}
// Force `UglyUrls` option to force `sitemap.xml` file name
- switch s.Target.(type) {
+ switch s.PageTarget().(type) {
case *target.Filesystem:
- s.Target.(*target.Filesystem).UglyUrls = true
+ s.PageTarget().(*target.PagePub).UglyUrls = true
optChanged = true
}
smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
- err := s.render("sitemap", n, "sitemap.xml", s.appendThemeTemplates(smLayouts)...)
+ b, err := s.renderXML("sitemap", n, s.appendThemeTemplates(smLayouts)...)
if err != nil {
return err
}
+ if err := s.WriteDestFile("sitemap.xml", b); err != nil {
+ return err
+ }
if optChanged {
- s.Target.(*target.Filesystem).UglyUrls = viper.GetBool("UglyUrls")
+ s.PageTarget().(*target.PagePub).UglyUrls = viper.GetBool("UglyUrls")
}
return nil
@@ -1087,20 +1114,24 @@
return found
}
-func (s *Site) render(name string, d interface{}, out string, layouts ...string) (err error) {
+func (s *Site) renderXML(name string, d interface{}, layouts ...string) (io.Reader, error) {
+ renderBuffer := s.NewXMLBuffer()
+ err := s.render(name, d, renderBuffer, layouts...)
+ return renderBuffer, err
+}
- layout, found := s.findFirstLayout(layouts...)
- if found == false {
- jww.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
- return
- }
+func (s *Site) renderPage(name string, d interface{}, layouts ...string) (io.Reader, error) {
+ renderBuffer := new(bytes.Buffer)
+ err := s.render(name, d, renderBuffer, layouts...)
+ var outBuffer = new(bytes.Buffer)
+
transformLinks := transform.NewEmptyTransforms()
if viper.GetBool("CanonifyUrls") {
absURL, err := transform.AbsURL(viper.GetString("BaseUrl"))
if err != nil {
- return err
+ return nil, err
}
transformLinks = append(transformLinks, absURL...)
}
@@ -1110,17 +1141,18 @@
}
transformer := transform.NewChain(transformLinks...)
+ transformer.Apply(outBuffer, renderBuffer)
+ return outBuffer, err
+}
- var renderBuffer *bytes.Buffer
-
- if strings.HasSuffix(out, ".xml") {
- renderBuffer = s.NewXMLBuffer()
- } else {
- renderBuffer = new(bytes.Buffer)
+func (s *Site) render(name string, d interface{}, renderBuffer *bytes.Buffer, layouts ...string) error {
+ layout, found := s.findFirstLayout(layouts...)
+ if found == false {
+ jww.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
+ return nil
}
- err = s.renderThing(d, layout, renderBuffer)
- if err != nil {
+ if err := s.renderThing(d, layout, renderBuffer); err != nil {
// Behavior here should be dependent on if running in server or watch mode.
jww.ERROR.Println(fmt.Errorf("Error while rendering %s: %v", name, err))
if !s.Running() {
@@ -1128,14 +1160,7 @@
}
}
- var outBuffer = new(bytes.Buffer)
- if strings.HasSuffix(out, ".xml") {
- outBuffer = renderBuffer
- } else {
- transformer.Apply(outBuffer, renderBuffer)
- }
-
- return s.WritePublic(out, outBuffer)
+ return nil
}
func (s *Site) findFirstLayout(layouts ...string) (string, bool) {
@@ -1160,33 +1185,48 @@
return bytes.NewBufferString(header)
}
-func (s *Site) initTarget() {
- if s.Target == nil {
- s.Target = &target.Filesystem{
+func (s *Site) PageTarget() target.Output {
+ if s.Targets.Page == nil {
+ s.Targets.Page = &target.PagePub{
PublishDir: s.absPublishDir(),
UglyUrls: viper.GetBool("UglyUrls"),
}
}
+ return s.Targets.Page
}
-func (s *Site) WritePublic(path string, reader io.Reader) (err error) {
- s.initTarget()
-
- jww.DEBUG.Println("writing to", path)
- return s.Target.Publish(path, reader)
+func (s *Site) FileTarget() target.Output {
+ if s.Targets.File == nil {
+ s.Targets.File = &target.Filesystem{
+ PublishDir: s.absPublishDir(),
+ }
+ }
+ return s.Targets.File
}
-func (s *Site) WriteAlias(path string, permalink template.HTML) (err error) {
- if s.Alias == nil {
- s.initTarget()
- s.Alias = &target.HTMLRedirectAlias{
+func (s *Site) AliasTarget() target.AliasPublisher {
+ if s.Targets.Alias == nil {
+ s.Targets.Alias = &target.HTMLRedirectAlias{
PublishDir: s.absPublishDir(),
}
+
}
+ return s.Targets.Alias
+}
- jww.DEBUG.Println("alias created at", path)
+func (s *Site) WriteDestFile(path string, reader io.Reader) (err error) {
+ jww.DEBUG.Println("creating file:", path)
+ return s.FileTarget().Publish(path, reader)
+}
- return s.Alias.Publish(path, permalink)
+func (s *Site) WriteDestPage(path string, reader io.Reader) (err error) {
+ jww.DEBUG.Println("creating page:", path)
+ return s.PageTarget().Publish(path, reader)
+}
+
+func (s *Site) WriteDestAlias(path string, permalink template.HTML) (err error) {
+ jww.DEBUG.Println("alias created at:", path)
+ return s.AliasTarget().Publish(path, permalink)
}
func (s *Site) draftStats() string {
--- a/target/file.go
+++ b/target/file.go
@@ -1,7 +1,6 @@
package target
import (
- "fmt"
"io"
"path"
@@ -23,13 +22,10 @@
}
type Filesystem struct {
- UglyUrls bool
- DefaultExtension string
- PublishDir string
+ PublishDir string
}
func (fs *Filesystem) Publish(path string, r io.Reader) (err error) {
-
translated, err := fs.Translate(path)
if err != nil {
return
@@ -39,42 +35,11 @@
}
func (fs *Filesystem) Translate(src string) (dest string, err error) {
- if src == "/" {
- if fs.PublishDir != "" {
- return path.Join(fs.PublishDir, "index.html"), nil
- }
- return "index.html", nil
- }
-
- dir, file := path.Split(src)
- ext := fs.extension(path.Ext(file))
- name := filename(file)
- if fs.PublishDir != "" {
- dir = path.Join(fs.PublishDir, dir)
- }
-
- if fs.UglyUrls || file == "index.html" {
- return path.Join(dir, fmt.Sprintf("%s%s", name, ext)), nil
- }
-
- return path.Join(dir, name, fmt.Sprintf("index%s", ext)), nil
+ return path.Join(fs.PublishDir, src), nil
}
func (fs *Filesystem) extension(ext string) string {
- switch ext {
- case ".md", ".rst": // TODO make this list configurable. page.go has the list of markup types.
- return ".html"
- }
-
- if ext != "" {
- return ext
- }
-
- if fs.DefaultExtension != "" {
- return fs.DefaultExtension
- }
-
- return ".html"
+ return ext
}
func filename(f string) string {
--- a/target/file_test.go
+++ /dev/null
@@ -1,93 +1,0 @@
-package target
-
-import (
- "testing"
-)
-
-func TestFileTranslator(t *testing.T) {
- tests := []struct {
- content string
- expected string
- }{
- {"/", "index.html"},
- {"index.html", "index.html"},
- {"bar/index.html", "bar/index.html"},
- {"foo", "foo/index.html"},
- {"foo.html", "foo/index.html"},
- {"foo.xhtml", "foo/index.xhtml"},
- {"section", "section/index.html"},
- {"section/", "section/index.html"},
- {"section/foo", "section/foo/index.html"},
- {"section/foo.html", "section/foo/index.html"},
- {"section/foo.rss", "section/foo/index.rss"},
- }
-
- for _, test := range tests {
- f := new(Filesystem)
- dest, err := f.Translate(test.content)
- if err != nil {
- t.Fatalf("Translate returned and unexpected err: %s", err)
- }
-
- if dest != test.expected {
- t.Errorf("Tranlate expected return: %s, got: %s", test.expected, dest)
- }
- }
-}
-
-func TestFileTranslatorBase(t *testing.T) {
- tests := []struct {
- content string
- expected string
- }{
- {"/", "a/base/index.html"},
- }
-
- for _, test := range tests {
- f := &Filesystem{PublishDir: "a/base"}
- fts := &Filesystem{PublishDir: "a/base/"}
-
- for _, fs := range []*Filesystem{f, fts} {
- dest, err := fs.Translate(test.content)
- if err != nil {
- t.Fatalf("Translated returned and err: %s", err)
- }
-
- if dest != test.expected {
- t.Errorf("Translate expected: %s, got: %s", test.expected, dest)
- }
- }
- }
-}
-
-func TestTranslateUglyUrls(t *testing.T) {
- tests := []struct {
- content string
- expected string
- }{
- {"foo.html", "foo.html"},
- {"/", "index.html"},
- {"section", "section.html"},
- {"index.html", "index.html"},
- }
-
- for _, test := range tests {
- f := &Filesystem{UglyUrls: true}
- dest, err := f.Translate(test.content)
- if err != nil {
- t.Fatalf("Translate returned an unexpected err: %s", err)
- }
-
- if dest != test.expected {
- t.Errorf("Translate expected return: %s, got: %s", test.expected, dest)
- }
- }
-}
-
-func TestTranslateDefaultExtension(t *testing.T) {
- f := &Filesystem{DefaultExtension: ".foobar"}
- dest, _ := f.Translate("baz")
- if dest != "baz/index.foobar" {
- t.Errorf("Translate expected return: %s, got %s", "baz/index.foobar", dest)
- }
-}
--- /dev/null
+++ b/target/page.go
@@ -1,0 +1,71 @@
+package target
+
+import (
+ "fmt"
+ "html/template"
+ "io"
+ "path"
+
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
+)
+
+type PagePublisher interface {
+ Translator
+ Publish(string, template.HTML) error
+}
+
+type PagePub struct {
+ UglyUrls bool
+ DefaultExtension string
+ PublishDir string
+}
+
+func (pp *PagePub) Publish(path string, r io.Reader) (err error) {
+
+ translated, err := pp.Translate(path)
+ if err != nil {
+ return
+ }
+
+ return helpers.WriteToDisk(translated, r, hugofs.DestinationFS)
+}
+
+func (pp *PagePub) Translate(src string) (dest string, err error) {
+ if src == "/" {
+ if pp.PublishDir != "" {
+ return path.Join(pp.PublishDir, "index.html"), nil
+ }
+ return "index.html", nil
+ }
+
+ dir, file := path.Split(src)
+ ext := pp.extension(path.Ext(file))
+ name := filename(file)
+ if pp.PublishDir != "" {
+ dir = path.Join(pp.PublishDir, dir)
+ }
+
+ if pp.UglyUrls || file == "index.html" {
+ return path.Join(dir, fmt.Sprintf("%s%s", name, ext)), nil
+ }
+
+ return path.Join(dir, name, fmt.Sprintf("index%s", ext)), nil
+}
+
+func (pp *PagePub) extension(ext string) string {
+ switch ext {
+ case ".md", ".rst": // TODO make this list configurable. page.go has the list of markup types.
+ return ".html"
+ }
+
+ if ext != "" {
+ return ext
+ }
+
+ if pp.DefaultExtension != "" {
+ return pp.DefaultExtension
+ }
+
+ return ".html"
+}
--- /dev/null
+++ b/target/page_test.go
@@ -1,0 +1,93 @@
+package target
+
+import (
+ "testing"
+)
+
+func TestPageTranslator(t *testing.T) {
+ tests := []struct {
+ content string
+ expected string
+ }{
+ {"/", "index.html"},
+ {"index.html", "index.html"},
+ {"bar/index.html", "bar/index.html"},
+ {"foo", "foo/index.html"},
+ {"foo.html", "foo/index.html"},
+ {"foo.xhtml", "foo/index.xhtml"},
+ {"section", "section/index.html"},
+ {"section/", "section/index.html"},
+ {"section/foo", "section/foo/index.html"},
+ {"section/foo.html", "section/foo/index.html"},
+ {"section/foo.rss", "section/foo/index.rss"},
+ }
+
+ for _, test := range tests {
+ f := new(PagePub)
+ dest, err := f.Translate(test.content)
+ if err != nil {
+ t.Fatalf("Translate returned and unexpected err: %s", err)
+ }
+
+ if dest != test.expected {
+ t.Errorf("Tranlate expected return: %s, got: %s", test.expected, dest)
+ }
+ }
+}
+
+func TestPageTranslatorBase(t *testing.T) {
+ tests := []struct {
+ content string
+ expected string
+ }{
+ {"/", "a/base/index.html"},
+ }
+
+ for _, test := range tests {
+ f := &PagePub{PublishDir: "a/base"}
+ fts := &PagePub{PublishDir: "a/base/"}
+
+ for _, fs := range []*PagePub{f, fts} {
+ dest, err := fs.Translate(test.content)
+ if err != nil {
+ t.Fatalf("Translated returned and err: %s", err)
+ }
+
+ if dest != test.expected {
+ t.Errorf("Translate expected: %s, got: %s", test.expected, dest)
+ }
+ }
+ }
+}
+
+func TestTranslateUglyUrls(t *testing.T) {
+ tests := []struct {
+ content string
+ expected string
+ }{
+ {"foo.html", "foo.html"},
+ {"/", "index.html"},
+ {"section", "section.html"},
+ {"index.html", "index.html"},
+ }
+
+ for _, test := range tests {
+ f := &PagePub{UglyUrls: true}
+ dest, err := f.Translate(test.content)
+ if err != nil {
+ t.Fatalf("Translate returned an unexpected err: %s", err)
+ }
+
+ if dest != test.expected {
+ t.Errorf("Translate expected return: %s, got: %s", test.expected, dest)
+ }
+ }
+}
+
+func TestTranslateDefaultExtension(t *testing.T) {
+ f := &PagePub{DefaultExtension: ".foobar"}
+ dest, _ := f.Translate("baz")
+ if dest != "baz/index.foobar" {
+ t.Errorf("Translate expected return: %s, got %s", "baz/index.foobar", dest)
+ }
+}