Compare commits

...

5 Commits

Author SHA1 Message Date
972c4d81f5 Cleanup 2024-02-16 22:27:10 -08:00
f0259bcbe9 Cleanup 2024-02-16 22:27:10 -08:00
e4eacea3dd Update README 2023-12-31 11:54:56 -08:00
d2531e07e1 Update README 2023-12-30 19:50:32 -08:00
9ecb479702 Update import path 2023-12-30 19:20:41 -08:00
9 changed files with 152 additions and 181 deletions

View File

@ -8,7 +8,7 @@ anything from blogs to image galleries using the same tool.
Goldsmith does not use any configuration files, and all behavior customization happens in code. Goldsmith uses the Goldsmith does not use any configuration files, and all behavior customization happens in code. Goldsmith uses the
[builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) to establish a chain, which modifies files as they pass [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) to establish a chain, which modifies files as they pass
through it. Although the [Goldsmith](https://godoc.org/foosoft.net/projects/goldsmith) API is short and (hopefully) easy through it. Although the [Goldsmith](https://godoc.org/git.foosoft.net/alex/goldsmith) API is short and (hopefully) easy
to understand, it is often best to learn by example: to understand, it is often best to learn by example:
1. Start by copying files from a source directory to a destination directory (the simplest possible use case): 1. Start by copying files from a source directory to a destination directory (the simplest possible use case):
@ -20,7 +20,7 @@ to understand, it is often best to learn by example:
``` ```
2. Now let's convert any Markdown files to HTML fragments (while still copying the rest), using the 2. Now let's convert any Markdown files to HTML fragments (while still copying the rest), using the
[Markdown](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/markdown) plugin: [Markdown](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/markdown) plugin:
```go ```go
goldsmith. goldsmith.
@ -32,7 +32,7 @@ to understand, it is often best to learn by example:
3. If we have any 3. If we have any
[front matter](https://raw.githubusercontent.com/FooSoft/goldsmith-samples/master/basic/content/index.md) in our [front matter](https://raw.githubusercontent.com/FooSoft/goldsmith-samples/master/basic/content/index.md) in our
Markdown files, we need to extract it using the, Markdown files, we need to extract it using the,
[FrontMatter](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/frontmatter) plugin: [FrontMatter](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/frontmatter) plugin:
```go ```go
goldsmith. goldsmith.
@ -45,7 +45,7 @@ to understand, it is often best to learn by example:
4. Next, we should run our barebones HTML through a 4. Next, we should run our barebones HTML through a
[template](https://raw.githubusercontent.com/FooSoft/goldsmith-samples/master/basic/content/layouts/basic.gohtml) to [template](https://raw.githubusercontent.com/FooSoft/goldsmith-samples/master/basic/content/layouts/basic.gohtml) to
add elements like a header, footer, or a menu; for this we can use the add elements like a header, footer, or a menu; for this we can use the
[Layout](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/frontmatter) plugin: [Layout](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/frontmatter) plugin:
```go ```go
goldsmith. goldsmith.
@ -58,7 +58,7 @@ to understand, it is often best to learn by example:
5. Now, let's [minify](https://en.wikipedia.org/wiki/Minification_(programming)) our files to reduce data transfer and 5. Now, let's [minify](https://en.wikipedia.org/wiki/Minification_(programming)) our files to reduce data transfer and
load times for our site's visitors using the load times for our site's visitors using the
[Minify](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/minify) plugin: [Minify](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/minify) plugin:
```go ```go
goldsmith. goldsmith.
@ -71,7 +71,7 @@ to understand, it is often best to learn by example:
``` ```
6. Debugging problems in minified code can be tricky, so let's use the 6. Debugging problems in minified code can be tricky, so let's use the
[Condition](https://godoc.org/foosoft.net/projects/goldsmith-components/filters/condition) filter to make [Condition](https://godoc.org/git.foosoft.net/alex/goldsmith-components/filters/condition) filter to make
minification occur only when we are ready for distribution. minification occur only when we are ready for distribution.
```go ```go
@ -87,7 +87,7 @@ to understand, it is often best to learn by example:
``` ```
7. Now that we have all of our plugins chained up, let's look at a complete example which uses 7. Now that we have all of our plugins chained up, let's look at a complete example which uses
[DevServer](https://godoc.org/foosoft.net/projects/goldsmith-components/devserver) to bootstrap a complete [DevServer](https://godoc.org/git.foosoft.net/alex/goldsmith-components/devserver) to bootstrap a complete
development sever which automatically rebuilds the site whenever source files are updated. development sever which automatically rebuilds the site whenever source files are updated.
```go ```go
@ -97,13 +97,13 @@ to understand, it is often best to learn by example:
"flag" "flag"
"log" "log"
"foosoft.net/projects/goldsmith" "git.foosoft.net/alex/goldsmith"
"foosoft.net/projects/goldsmith-components/devserver" "git.foosoft.net/alex/goldsmith-components/devserver"
"foosoft.net/projects/goldsmith-components/filters/condition" "git.foosoft.net/alex/goldsmith-components/filters/condition"
"foosoft.net/projects/goldsmith-components/plugins/frontmatter" "git.foosoft.net/alex/goldsmith-components/plugins/frontmatter"
"foosoft.net/projects/goldsmith-components/plugins/layout" "git.foosoft.net/alex/goldsmith-components/plugins/layout"
"foosoft.net/projects/goldsmith-components/plugins/markdown" "git.foosoft.net/alex/goldsmith-components/plugins/markdown"
"foosoft.net/projects/goldsmith-components/plugins/minify" "git.foosoft.net/alex/goldsmith-components/plugins/minify"
) )
type builder struct { type builder struct {
@ -139,12 +139,12 @@ to understand, it is often best to learn by example:
Below are some examples of Goldsmith usage which can used to base your site on: Below are some examples of Goldsmith usage which can used to base your site on:
* [Basic Sample](https://github.com/FooSoft/goldsmith-samples/tree/master/basic): a great starting point, this is the * [Basic Sample](https://git.foosoft.net/alex/goldsmith-samples/src/branch/master/basic): a great starting point, this
sample site from the tutorial. is the sample site from the tutorial.
* [Bootstrap Sample](https://github.com/FooSoft/goldsmith-samples/tree/master/bootstrap): a slightly more advanced * [Bootstrap Sample](https://git.foosoft.net/alex/goldsmith-samples/src/branch/master/bootstrap): a slightly more
sample using [Bootstrap](https://getbootstrap.com/). advanced sample using [Bootstrap](https://getbootstrap.com/).
* [FooSoft.net](https://foosoft.net/projects/goldsmith): I've been "dogfooding" Goldsmith by using it to build * [FooSoft.net](https://git.foosoft.net/alex/goldsmith): I've been "dogfooding" Goldsmith by using it to [generate my
my homepage for years. homepage](/posts/generating-the-foosoft.net-homepage) for nearly a decade.
## Components ## Components
@ -152,55 +152,55 @@ A growing set of plugins, filters, and other tools are provided to make it easie
### Plugins ### Plugins
* [Absolute](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/absolute): Convert relative HTML file * [Absolute](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/absolute): Convert relative HTML file
references to absolute paths. references to absolute paths.
* [Breadcrumbs](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/breadcrumbs): Generate metadata * [Breadcrumbs](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/breadcrumbs): Generate metadata
required to build breadcrumb navigation. required to build breadcrumb navigation.
* [Collection](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/collection): Group related pages * [Collection](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/collection): Group related pages
into named collections. into named collections.
* [Document](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/document): Enable simple DOM * [Document](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/document): Enable simple DOM
modification via an API similar to jQuery. modification via an API similar to jQuery.
* [Forward](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/forward): Create simple redirections * [Forward](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/forward): Create simple redirections
for pages that have moved to a new URL. for pages that have moved to a new URL.
* [FrontMatter](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/frontmatter): Extract the * [FrontMatter](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/frontmatter): Extract the
JSON, YAML, or TOML metadata stored in your files. JSON, YAML, or TOML metadata stored in your files.
* [Index](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/index): Create metadata for directory * [Index](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/index): Create metadata for directory
file listings and generate directory index pages. file listings and generate directory index pages.
* [Layout](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/layout): Transform your HTML files with * [Layout](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/layout): Transform your HTML files with
Go templates. Go templates.
* [LiveJs](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/livejs): Inject JavaScript code to * [LiveJs](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/livejs): Inject JavaScript code to
automatically reload pages when modified. automatically reload pages when modified.
* [Markdown](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/markdown): Render Markdown documents * [Markdown](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/markdown): Render Markdown documents
as HTML fragments. as HTML fragments.
* [Minify](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/minify): Remove superfluous data from a * [Minify](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/minify): Remove superfluous data from a
variety of web formats. variety of web formats.
* [Pager](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/pager): Split arrays of metadata into * [Pager](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/pager): Split arrays of metadata into
standalone pages. standalone pages.
* [Rule](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/rule): Update metadata and filter files * [Rule](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/rule): Update metadata and filter files
based on paths. based on paths.
* [Summary](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/summary): Extract summary and title * [Summary](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/summary): Extract summary and title
metadata from HTML files. metadata from HTML files.
* [Syndicate](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/syndicate): Generate RSS, Atom, and * [Syndicate](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/syndicate): Generate RSS, Atom, and
JSON feeds from existing metadata. JSON feeds from existing metadata.
* [Syntax](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/syntax): Enable syntax highlighting for * [Syntax](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/syntax): Enable syntax highlighting for
pre-formatted code blocks. pre-formatted code blocks.
* [Tags](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/tags): Generate tag clouds and indices * [Tags](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/tags): Generate tag clouds and indices
from file metadata. from file metadata.
* [Thumbnail](https://godoc.org/foosoft.net/projects/goldsmith-components/plugins/thumbnail): Build thumbnails for a * [Thumbnail](https://godoc.org/git.foosoft.net/alex/goldsmith-components/plugins/thumbnail): Build thumbnails for a
variety of common image formats. variety of common image formats.
### Filters ### Filters
* [Condition](https://godoc.org/foosoft.net/projects/goldsmith-components/filters/condition): Filter files based on a * [Condition](https://godoc.org/git.foosoft.net/alex/goldsmith-components/filters/condition): Filter files based on a
single condition. single condition.
* [Operator](https://godoc.org/foosoft.net/projects/goldsmith-components/filters/operator): Join filters using * [Operator](https://godoc.org/git.foosoft.net/alex/goldsmith-components/filters/operator): Join filters using
logical `AND`, `OR`, and `NOT` operators. logical `AND`, `OR`, and `NOT` operators.
* [Wildcard](https://godoc.org/foosoft.net/projects/goldsmith-components/filters/wildcard): Filter files using path * [Wildcard](https://godoc.org/git.foosoft.net/alex/goldsmith-components/filters/wildcard): Filter files using path
wildcards (`*`, `?`, etc.) wildcards (`*`, `?`, etc.)
### Other ### Other
* [DevServer](https://godoc.org/foosoft.net/projects/goldsmith-components/devserver): Simple framework for building, * [DevServer](https://godoc.org/git.foosoft.net/alex/goldsmith-components/devserver): Simple framework for building,
updating, and viewing your site. updating, and viewing your site.
* [Harness](https://godoc.org/foosoft.net/projects/goldsmith-components/harness): Unit test harness for verifying * [Harness](https://godoc.org/git.foosoft.net/alex/goldsmith-components/harness): Unit test harness for verifying
Goldsmith plugins and filters. Goldsmith plugins and filters.

View File

@ -8,6 +8,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings"
) )
type cache struct { type cache struct {
@ -72,7 +73,9 @@ func (self *cache) buildCachePath(context *Context, outputPath string, inputFile
hasher := crc32.NewIEEE() hasher := crc32.NewIEEE()
hasher.Write([]byte(outputPath)) hasher.Write([]byte(outputPath))
sort.Sort(filesByPath(inputFiles)) sort.Slice(inputFiles, func(i, j int) bool {
return strings.Compare(inputFiles[i].Path(), inputFiles[j].Path()) < 0
})
for _, inputFile := range inputFiles { for _, inputFile := range inputFiles {
modTimeBuff := make([]byte, 8) modTimeBuff := make([]byte, 8)

66
file_exporter.go Normal file
View File

@ -0,0 +1,66 @@
package goldsmith
import (
"os"
"path/filepath"
)
type fileExporter struct {
targetDir string
clean bool
tokens map[string]bool
}
func (*fileExporter) Name() string {
return "exporter"
}
func (self *fileExporter) Initialize(context *Context) error {
self.tokens = make(map[string]bool)
context.Threads(1)
return nil
}
func (self *fileExporter) Process(context *Context, file *File) error {
slicePath := func(path string) string {
if filepath.IsAbs(path) {
var err error
if path, err = filepath.Rel("/", path); err != nil {
panic(err)
}
}
return filepath.Clean(path)
}
for token := slicePath(file.relPath); token != "."; token = filepath.Dir(token) {
self.tokens[token] = true
}
return file.export(self.targetDir)
}
func (self *fileExporter) Finalize(context *Context) error {
if !self.clean {
return nil
}
return filepath.Walk(self.targetDir, func(path string, info os.FileInfo, err error) error {
if path == self.targetDir {
return nil
}
relPath, err := filepath.Rel(self.targetDir, path)
if err != nil {
panic(err)
}
if tokenized, _ := self.tokens[relPath]; !tokenized {
if err := os.RemoveAll(path); err != nil {
return err
}
}
return nil
})
}

35
file_importer.go Normal file
View File

@ -0,0 +1,35 @@
package goldsmith
import (
"os"
"path/filepath"
)
type fileImporter struct {
sourceDir string
}
func (*fileImporter) Name() string {
return "importer"
}
func (self *fileImporter) Initialize(context *Context) error {
return filepath.Walk(self.sourceDir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
relPath, err := filepath.Rel(self.sourceDir, path)
if err != nil {
panic(err)
}
file, err := context.CreateFileFromAsset(relPath, path)
if err != nil {
return err
}
context.DispatchFile(file)
return nil
})
}

View File

@ -1,49 +0,0 @@
package goldsmith
import (
"os"
"path/filepath"
"strings"
)
type filesByPath []*File
func (self filesByPath) Len() int {
return len(self)
}
func (self filesByPath) Swap(i, j int) {
self[i], self[j] = self[j], self[i]
}
func (self filesByPath) Less(i, j int) bool {
return strings.Compare(self[i].Path(), self[j].Path()) < 0
}
type fileInfo struct {
os.FileInfo
path string
}
func cleanPath(path string) string {
if filepath.IsAbs(path) {
var err error
if path, err = filepath.Rel("/", path); err != nil {
panic(err)
}
}
return filepath.Clean(path)
}
func scanDir(rootDir string, infos chan fileInfo) {
defer close(infos)
filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if err == nil {
infos <- fileInfo{FileInfo: info, path: path}
}
return err
})
}

2
go.mod
View File

@ -1,3 +1,3 @@
module foosoft.net/projects/goldsmith module git.foosoft.net/alex/goldsmith
go 1.13 go 1.13

View File

@ -8,9 +8,6 @@ import (
// Goldsmith chainable context. // Goldsmith chainable context.
type Goldsmith struct { type Goldsmith struct {
sourceDir string
targetDir string
contexts []*Context contexts []*Context
cache *cache cache *cache
@ -24,8 +21,8 @@ type Goldsmith struct {
// Begin starts a chain, reading the files located in the source directory as input. // Begin starts a chain, reading the files located in the source directory as input.
func Begin(sourceDir string) *Goldsmith { func Begin(sourceDir string) *Goldsmith {
goldsmith := &Goldsmith{sourceDir: sourceDir} goldsmith := new(Goldsmith)
goldsmith.Chain(&loader{}) goldsmith.Chain(&fileImporter{sourceDir: sourceDir})
return goldsmith return goldsmith
} }
@ -77,9 +74,7 @@ func (self *Goldsmith) FilterPop() *Goldsmith {
// End stops a chain, writing all recieved files to targetDir as output. // End stops a chain, writing all recieved files to targetDir as output.
func (self *Goldsmith) End(targetDir string) []error { func (self *Goldsmith) End(targetDir string) []error {
self.targetDir = targetDir self.Chain(&fileExporter{targetDir: targetDir, clean: self.clean})
self.Chain(&saver{clean: self.clean})
for _, context := range self.contexts { for _, context := range self.contexts {
go context.step() go context.step()
} }

View File

@ -1,30 +0,0 @@
package goldsmith
import "path/filepath"
type loader struct{}
func (*loader) Name() string {
return "loader"
}
func (*loader) Initialize(context *Context) error {
scannedInfo := make(chan fileInfo)
go scanDir(context.goldsmith.sourceDir, scannedInfo)
for info := range scannedInfo {
if info.IsDir() {
continue
}
relPath, _ := filepath.Rel(context.goldsmith.sourceDir, info.path)
file, err := context.CreateFileFromAsset(relPath, info.path)
if err != nil {
return err
}
context.DispatchFile(file)
}
return nil
}

View File

@ -1,49 +0,0 @@
package goldsmith
import (
"os"
"path/filepath"
)
type saver struct {
clean bool
tokens map[string]bool
}
func (*saver) Name() string {
return "saver"
}
func (self *saver) Initialize(context *Context) error {
self.tokens = make(map[string]bool)
context.Threads(1)
return nil
}
func (self *saver) Process(context *Context, file *File) error {
for token := cleanPath(file.relPath); token != "."; token = filepath.Dir(token) {
self.tokens[token] = true
}
return file.export(context.goldsmith.targetDir)
}
func (self *saver) Finalize(context *Context) error {
if !self.clean {
return nil
}
scannedInfo := make(chan fileInfo)
go scanDir(context.goldsmith.targetDir, scannedInfo)
for info := range scannedInfo {
if info.path != context.goldsmith.targetDir {
relPath, _ := filepath.Rel(context.goldsmith.targetDir, info.path)
if contained, _ := self.tokens[relPath]; !contained {
os.RemoveAll(info.path)
}
}
}
return nil
}