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:
parent
0b653d6b24
commit
2dc46e00da
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user