From 7b960ac121fa8cdbd9b52c4f2adf66d39440758e Mon Sep 17 00:00:00 2001 From: spf13 Date: Tue, 4 Nov 2014 00:39:37 -0500 Subject: [PATCH] New targets & new renderers and write methods [WIP] --- hugolib/planner.go | 8 +- hugolib/site.go | 178 ++++++++++++++++---------- target/file.go | 41 +----- target/page.go | 71 ++++++++++ target/{file_test.go => page_test.go} | 16 +-- 5 files changed, 195 insertions(+), 119 deletions(-) create mode 100644 target/page.go rename target/{file_test.go => page_test.go} (85%) diff --git a/hugolib/planner.go b/hugolib/planner.go index ae32f9a38..7f03bbb0a 100644 --- a/hugolib/planner.go +++ b/hugolib/planner.go @@ -24,23 +24,23 @@ func (s *Site) ShowPlan(out io.Writer) (err error) { } 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 } diff --git a/hugolib/site.go b/hugolib/site.go index d71eda359..2e26beb84 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -69,8 +69,7 @@ type Site struct { 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 @@ type Site struct { 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) Build() (err error) { func (s *Site) Analyze() { s.Process() - s.initTarget() - s.Alias = &target.HTMLRedirectAlias{ - PublishDir: s.absPublishDir(), - } s.ShowPlan(os.Stdout) } @@ -671,7 +672,7 @@ func (s *Site) RenderAliases() error { 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 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa 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,23 +849,32 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error, 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") { // 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 @@ func (s *Site) RenderListsOfTaxonomyTerms() (err error) { 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 @@ func (s *Site) RenderSectionLists() error { 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) newHomeNode() *Node { 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,19 +980,13 @@ func (s *Site) RenderHomePage() error { 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 } - } - } - - // 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 }() + if err := s.WriteDestFile("rss.xml", b); err != nil { + return err + } } } @@ -974,10 +995,13 @@ func (s *Site) RenderHomePage() error { 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 @@ func (s *Site) RenderSitemap() error { } // 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 @@ func (s *Site) layoutExists(layouts ...string) bool { 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 @@ func (s *Site) render(name string, d interface{}, out string, layouts ...string) } 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 @@ func (s *Site) render(name string, d interface{}, out string, layouts ...string) } } - 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 @@ func (s *Site) NewXMLBuffer() *bytes.Buffer { 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) WriteAlias(path string, permalink template.HTML) (err error) { - if s.Alias == nil { - s.initTarget() - s.Alias = &target.HTMLRedirectAlias{ +func (s *Site) FileTarget() target.Output { + if s.Targets.File == nil { + s.Targets.File = &target.Filesystem{ PublishDir: s.absPublishDir(), } } + return s.Targets.File +} - jww.DEBUG.Println("alias created at", path) +func (s *Site) AliasTarget() target.AliasPublisher { + if s.Targets.Alias == nil { + s.Targets.Alias = &target.HTMLRedirectAlias{ + PublishDir: s.absPublishDir(), + } - return s.Alias.Publish(path, permalink) + } + return s.Targets.Alias +} + +func (s *Site) WriteDestFile(path string, reader io.Reader) (err error) { + jww.DEBUG.Println("creating file:", path) + return s.FileTarget().Publish(path, reader) +} + +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 { diff --git a/target/file.go b/target/file.go index fa4e79681..37851cae2 100644 --- a/target/file.go +++ b/target/file.go @@ -1,7 +1,6 @@ package target import ( - "fmt" "io" "path" @@ -23,13 +22,10 @@ type Output interface { } 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) Publish(path string, r io.Reader) (err error) { } 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 { diff --git a/target/page.go b/target/page.go new file mode 100644 index 000000000..32bb56ded --- /dev/null +++ b/target/page.go @@ -0,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" +} diff --git a/target/file_test.go b/target/page_test.go similarity index 85% rename from target/file_test.go rename to target/page_test.go index 258eae41c..69ad1cfa2 100644 --- a/target/file_test.go +++ b/target/page_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestFileTranslator(t *testing.T) { +func TestPageTranslator(t *testing.T) { tests := []struct { content string expected string @@ -23,7 +23,7 @@ func TestFileTranslator(t *testing.T) { } for _, test := range tests { - f := new(Filesystem) + f := new(PagePub) dest, err := f.Translate(test.content) if err != nil { t.Fatalf("Translate returned and unexpected err: %s", err) @@ -35,7 +35,7 @@ func TestFileTranslator(t *testing.T) { } } -func TestFileTranslatorBase(t *testing.T) { +func TestPageTranslatorBase(t *testing.T) { tests := []struct { content string expected string @@ -44,10 +44,10 @@ func TestFileTranslatorBase(t *testing.T) { } for _, test := range tests { - f := &Filesystem{PublishDir: "a/base"} - fts := &Filesystem{PublishDir: "a/base/"} + f := &PagePub{PublishDir: "a/base"} + fts := &PagePub{PublishDir: "a/base/"} - for _, fs := range []*Filesystem{f, fts} { + for _, fs := range []*PagePub{f, fts} { dest, err := fs.Translate(test.content) if err != nil { t.Fatalf("Translated returned and err: %s", err) @@ -72,7 +72,7 @@ func TestTranslateUglyUrls(t *testing.T) { } for _, test := range tests { - f := &Filesystem{UglyUrls: true} + f := &PagePub{UglyUrls: true} dest, err := f.Translate(test.content) if err != nil { t.Fatalf("Translate returned an unexpected err: %s", err) @@ -85,7 +85,7 @@ func TestTranslateUglyUrls(t *testing.T) { } func TestTranslateDefaultExtension(t *testing.T) { - f := &Filesystem{DefaultExtension: ".foobar"} + 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)