1
0
mirror of https://github.com/danog/gojekyll.git synced 2025-01-22 12:51:24 +01:00

Incremental pt. 2

This commit is contained in:
Oliver Steele 2017-07-25 11:08:53 -04:00
parent 0b653d6b24
commit 2dc46e00da
11 changed files with 126 additions and 34 deletions

View File

@ -112,6 +112,11 @@ func (c *Config) IsConfigPath(rel string) bool {
return rel == "_config.yml"
}
// SassDir returns the relative path of the SASS directory.
func (c *Config) SassDir() string {
return "_sass"
}
// SourceDir returns the source directory as an absolute path.
func (c *Config) SourceDir() string {
return utils.MustAbs(c.Source)

View File

@ -12,7 +12,7 @@ import (
// file is embedded in StaticFile and page
type file struct {
site Site
filename string // target os filepath
filename string // target filepath
relpath string // slash-separated path relative to site or container source
outputExt string
permalink string // cached permalink
@ -20,15 +20,6 @@ type file struct {
frontMatter map[string]interface{}
}
func (f *file) String() string {
return fmt.Sprintf("%T{Path=%v, Permalink=%v}", f, f.relpath, f.permalink)
}
func (f *file) OutputExt() string { return f.outputExt }
func (f *file) Permalink() string { return f.permalink }
func (f *file) Published() bool { return templates.VariableMap(f.frontMatter).Bool("published", true) }
func (f *file) SourcePath() string { return f.filename }
// NewFile creates a Page or StaticFile.
//
// filename is the absolute filename. relpath is the path relative to the site or collection directory.
@ -57,3 +48,23 @@ func NewFile(s Site, filename string, relpath string, defaults map[string]interf
p := &StaticFile{fields}
return p, nil
}
func (f *file) String() string {
return fmt.Sprintf("%T{Path=%v, Permalink=%v}", f, f.relpath, f.permalink)
}
func (f *file) OutputExt() string { return f.outputExt }
func (f *file) Permalink() string { return f.permalink }
func (f *file) Published() bool { return templates.VariableMap(f.frontMatter).Bool("published", true) }
func (f *file) SourcePath() string { return f.filename }
// const requiresReloadError = error.Error("requires reload")
func (f *file) Reload() error {
info, err := os.Stat(f.filename)
if err != nil {
return err
}
f.fileModTime = info.ModTime()
return nil
}

View File

@ -18,6 +18,8 @@ type Document interface {
Published() bool
Static() bool
Write(io.Writer) error
Reload() error
}
// Site is the interface that the site provides to a page.

View File

@ -42,27 +42,49 @@ type page struct {
func (p *page) Static() bool { return false }
func makePage(filename string, f file) (*page, error) {
b, err := ioutil.ReadFile(filename)
raw, lineNo, err := readFrontMatter(&f)
if err != nil {
return nil, err
}
lineNo := 1
frontMatter, err := frontmatter.Read(&b, &lineNo)
if err != nil {
return nil, err
}
f.frontMatter = templates.MergeVariableMaps(f.frontMatter, frontMatter)
p := page{
file: f,
firstLine: lineNo,
raw: b,
raw: raw,
}
if err = p.setPermalink(); err != nil {
if err := p.setPermalink(); err != nil {
return nil, err
}
return &p, nil
}
func (p *page) Reload() error {
if err := p.file.Reload(); err != nil {
return err
}
// FIXME use original defaults
raw, lineNo, err := readFrontMatter(&p.file)
if err != nil {
return err
}
p.firstLine = lineNo
p.raw = raw
return nil
}
func readFrontMatter(f *file) (b []byte, lineNo int, err error) {
b, err = ioutil.ReadFile(f.filename)
if err != nil {
return
}
lineNo = 1
frontMatter, err := frontmatter.Read(&b, &lineNo)
if err != nil {
return
}
f.frontMatter = templates.MergeVariableMaps(f.frontMatter, frontMatter)
return
}
func (p *page) FrontMatter() map[string]interface{} {
return p.frontMatter
}

View File

@ -5,7 +5,7 @@ import (
"os"
)
// StaticFile is a static file.
// A StaticFile is a static file. (Lint made me say this.)
type StaticFile struct {
file
}

View File

@ -65,6 +65,7 @@ func (d *feedDoc) SourcePath() string { return "" }
func (d *feedDoc) OutputExt() string { return ".xml" }
func (d *feedDoc) Published() bool { return true }
func (d *feedDoc) Static() bool { return false } // FIXME means different things to different callers
func (d *feedDoc) Reload() error { return nil }
func (d *feedDoc) Content() []byte {
bindings := map[string]interface{}{"site": d.site}

View File

@ -104,6 +104,7 @@ func (d *redirectionDoc) SourcePath() string { return "" } // FIXME bad design
func (d *redirectionDoc) OutputExt() string { return ".html" }
func (d *redirectionDoc) Published() bool { return true }
func (d *redirectionDoc) Static() bool { return false } // FIXME means different things to different callers
func (d *redirectionDoc) Reload() error { return nil }
func (d *redirectionDoc) Content() []byte {
buf := new(bytes.Buffer)

View File

@ -18,6 +18,7 @@ func (d *templateDoc) SourcePath() string { return "" }
func (d *templateDoc) OutputExt() string { return path.Ext(d.path) }
func (d *templateDoc) Published() bool { return true }
func (d *templateDoc) Static() bool { return false } // FIXME means different things to different callers
func (d *templateDoc) Reload() error { return nil }
func (d *templateDoc) Content() []byte {
bindings := map[string]interface{}{"site": d.site}

View File

@ -5,6 +5,9 @@ import (
"os"
"strings"
"time"
"github.com/osteele/gojekyll/pages"
"github.com/osteele/gojekyll/utils"
)
// WatchRebuild watches the site directory. Each time a file changes, it
@ -52,23 +55,47 @@ func (s *Site) processFilesEvent(fileset FilesEvent, messages chan<- interface{}
return s
}
elapsed := time.Since(start)
messages <- fmt.Sprintf("wrote %d files in %.2fs.\n", count, elapsed.Seconds())
inflect := map[bool]string{true: "", false: "s"}[count == 1]
messages <- fmt.Sprintf("wrote %d file%s in %.2fs.\n", count, inflect, elapsed.Seconds())
return r
}
// reloads and rebuilds the site; returns a copy and count
func (s *Site) rebuild(paths []string) (*Site, int, error) {
func (s *Site) rebuild(paths []string) (r *Site, n int, err error) {
if s.requiresFullReload(paths) {
r, err := s.Reloaded(paths)
r, err = s.Reloaded(paths)
if err != nil {
return nil, 0, err
return
}
n, err := r.Build()
return r, n, err
n, err = r.Build()
return
}
return s, 0, nil
r = s
pathSet := utils.StringSet(paths)
for _, d := range s.docs {
if s.invalidatesDoc(pathSet, d) {
err = d.Reload()
if err != nil {
return
}
err = s.WriteDoc(d)
if err != nil {
return
}
n++
}
}
return
}
// Return true if a source file requires a full reload / rebuild.
//
// This is always true outside of incremental mode, since even a
// static asset can cause pages to change if they reference its
// variables.
//
// This function works on relative paths. It does not work for theme
// sources.
func (s *Site) requiresFullReload(paths []string) bool {
for _, path := range paths {
switch {
@ -78,6 +105,8 @@ func (s *Site) requiresFullReload(paths []string) bool {
return false
case !s.config.Incremental:
return true
case strings.HasPrefix(path, s.config.SassDir()):
return true
case strings.HasPrefix(path, s.config.DataDir):
return true
case strings.HasPrefix(path, s.config.LayoutsDir):
@ -87,23 +116,32 @@ func (s *Site) requiresFullReload(paths []string) bool {
return false
}
// relativize and de-dup filenames, and filter to those that affect the build
func (s *Site) affectsBuildFilter(filenames []string) []string {
// De-dup relative paths, and filter to those that might affect the build.
//
// Site watch uses this to decide when to send events.
func (s *Site) affectsBuildFilter(paths []string) []string {
var (
result = make([]string, 0, len(filenames))
result = make([]string, 0, len(paths))
seen = map[string]bool{}
)
loop:
for _, path := range filenames {
for _, path := range paths {
switch {
case s.config.IsConfigPath(path):
// break
case s.Exclude(path):
continue loop
case seen[path]:
continue loop
}
seen[path] = true
result = append(result, path)
seen[path] = true
}
return result
}
// returns true if changes to the site-relative paths invalidate doc
func (s *Site) invalidatesDoc(paths map[string]bool, d pages.Document) bool {
rel := utils.MustRel(s.SourceDir(), d.SourcePath())
return paths[rel]
}

View File

@ -49,10 +49,12 @@ func (s *Site) WatchFiles() (<-chan FilesEvent, error) {
}
func (s *Site) makeFileWatcher() (<-chan string, error) {
if s.config.ForcePolling {
switch {
case s.config.ForcePolling:
return s.makePollingWatcher()
default:
return s.makeEventWatcher()
}
return s.makeEventWatcher()
}
func (s *Site) makePollingWatcher() (<-chan string, error) {

View File

@ -12,3 +12,12 @@ func SearchStrings(array []string, s string) bool {
}
return false
}
// StringSet creates a characteristic function map that tests for presence in an array.
func StringSet(array []string) map[string]bool {
set := map[string]bool{}
for _, s := range array {
set[s] = true
}
return set
}