about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2024-06-23 17:22:09 +0200
committerAlan Pearce2024-06-23 19:23:20 +0200
commitfe4a23b550b7450e6e24cd3c78dfd575122d361d (patch)
tree61e4f2158455ad8e7c05a7c8f219e909a3239263
parent3a7e71544045647fa3ac702a1f0c5ad14623cb98 (diff)
downloadwebsite-fe4a23b550b7450e6e24cd3c78dfd575122d361d.tar.lz
website-fe4a23b550b7450e6e24cd3c78dfd575122d361d.tar.zst
website-fe4a23b550b7450e6e24cd3c78dfd575122d361d.zip
pre-compress generated files in production
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--internal/builder/builder.go24
-rw-r--r--internal/builder/files.go83
-rw-r--r--nix/gomod2nix.toml3
5 files changed, 104 insertions, 9 deletions
diff --git a/go.mod b/go.mod
index 5660e67..8595437 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
 	github.com/a-h/htmlformat v0.0.0-20231108124658-5bd994fe268e
 	github.com/a-h/templ v0.2.707
 	github.com/adrg/frontmatter v0.2.0
+	github.com/andybalholm/brotli v1.1.0
 	github.com/antchfx/xmlquery v1.4.0
 	github.com/antchfx/xpath v1.3.0
 	github.com/ardanlabs/conf/v3 v3.1.7
diff --git a/go.sum b/go.sum
index 398c65f..a8bf342 100644
--- a/go.sum
+++ b/go.sum
@@ -11,6 +11,8 @@ github.com/adrg/frontmatter v0.2.0 h1:/DgnNe82o03riBd1S+ZDjd43wAmC6W35q67NHeLkPd
 github.com/adrg/frontmatter v0.2.0/go.mod h1:93rQCj3z3ZlwyxxpQioRKC1wDLto4aXHrbqIsnH9wmE=
 github.com/alanpearce/htmlformat v0.0.0-20240425000139-1244374b2562 h1:7LpBXZnmFk8+RwdFnAYB7rKZhBQrQ4poPLEhpwwbmSc=
 github.com/alanpearce/htmlformat v0.0.0-20240425000139-1244374b2562/go.mod h1:FMIm5afKmEfarNbIXOaPHFY8X7fo+fRQB6I9MPG2nB0=
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
 github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
 github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
 github.com/antchfx/xmlquery v1.4.0 h1:xg2HkfcRK2TeTbdb0m1jxCYnvsPaGY/oeZWTGqX/0hA=
diff --git a/internal/builder/builder.go b/internal/builder/builder.go
index 44ab402..4f305db 100644
--- a/internal/builder/builder.go
+++ b/internal/builder/builder.go
@@ -32,30 +32,34 @@ type Result struct {
 	Hashes []string
 }
 
+var compressFiles = false
+
 func mkdirp(dirs ...string) error {
 	err := os.MkdirAll(path.Join(dirs...), 0755)
 
 	return errors.Wrap(err, "could not create directory")
 }
 
-func outputToFile(output io.Reader, filename ...string) error {
-	// log.Debug("outputting file", "filename", path.Join(filename...))
-	file, err := os.OpenFile(path.Join(filename...), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+func outputToFile(output io.Reader, pathParts ...string) error {
+	filename := path.Join(pathParts...)
+	// log.Debug("outputting file", "filename", filename)
+	file, err := openFileAndVariants(filename)
 	if err != nil {
 		return errors.WithMessage(err, "could not open output file")
 	}
 	defer file.Close()
 
-	if _, err := file.ReadFrom(output); err != nil {
+	if _, err := io.Copy(file, output); err != nil {
 		return errors.WithMessage(err, "could not write output file")
 	}
 
 	return nil
 }
 
-func renderToFile(component templ.Component, filename ...string) error {
-	// log.Debug("outputting file", "filename", path.Join(filename...))
-	file, err := os.OpenFile(path.Join(filename...), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+func renderToFile(component templ.Component, pathParts ...string) error {
+	filename := path.Join(pathParts...)
+	// log.Debug("outputting file", "filename", filename)
+	file, err := openFileAndVariants(filename)
 	if err != nil {
 		return errors.WithMessage(err, "could not open output file")
 	}
@@ -68,9 +72,10 @@ func renderToFile(component templ.Component, filename ...string) error {
 	return nil
 }
 
-func writerToFile(writer io.WriterTo, filename ...string) error {
+func writerToFile(writer io.WriterTo, pathParts ...string) error {
+	filename := path.Join(pathParts...)
 	// log.Debug("outputting file", "filename", path.Join(filename...))
-	file, err := os.OpenFile(path.Join(filename...), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+	file, err := openFileAndVariants(filename)
 	if err != nil {
 		return errors.WithMessage(err, "could not open output file")
 	}
@@ -252,6 +257,7 @@ func BuildSite(ioConfig IOConfig) (*Result, error) {
 		return nil, errors.WithMessage(err, "could not get config")
 	}
 	config.InjectLiveReload = ioConfig.Development
+	compressFiles = !ioConfig.Development
 
 	if ioConfig.BaseURL.URL != nil {
 		config.BaseURL.URL, err = url.Parse(ioConfig.BaseURL.String())
diff --git a/internal/builder/files.go b/internal/builder/files.go
new file mode 100644
index 0000000..57d4d2e
--- /dev/null
+++ b/internal/builder/files.go
@@ -0,0 +1,83 @@
+package builder
+
+import (
+	"compress/gzip"
+	"io"
+	"os"
+
+	"github.com/andybalholm/brotli"
+)
+
+const (
+	gzipLevel   = 6
+	brotliLevel = 9
+)
+
+type MultiWriteCloser struct {
+	writers     []io.WriteCloser
+	multiWriter io.Writer
+}
+
+func (mw *MultiWriteCloser) Write(p []byte) (n int, err error) {
+	return mw.multiWriter.Write(p)
+}
+
+func (mw *MultiWriteCloser) Close() error {
+	var lastErr error
+	for _, w := range mw.writers {
+		err := w.Close()
+		if err != nil {
+			lastErr = err
+		}
+	}
+	return lastErr
+}
+
+func openFileWrite(filename string) (*os.File, error) {
+	return os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+}
+
+func openFileGz(filename string) (*gzip.Writer, error) {
+	filenameGz := filename + ".gz"
+	f, err := openFileWrite(filenameGz)
+	if err != nil {
+		return nil, err
+	}
+	return gzip.NewWriterLevel(f, gzipLevel)
+}
+
+func openFileBrotli(filename string) (*brotli.Writer, error) {
+	filenameBrotli := filename + ".br"
+	f, err := openFileWrite(filenameBrotli)
+	if err != nil {
+		return nil, err
+	}
+	return brotli.NewWriterLevel(f, brotliLevel), nil
+}
+
+func multiOpenFile(filename string) (*MultiWriteCloser, error) {
+	r, err := openFileWrite(filename)
+	if err != nil {
+		return nil, err
+	}
+	gz, err := openFileGz(filename)
+	if err != nil {
+		return nil, err
+	}
+	br, err := openFileBrotli(filename)
+	if err != nil {
+		return nil, err
+	}
+	return &MultiWriteCloser{
+		writers:     []io.WriteCloser{r, gz, br},
+		multiWriter: io.MultiWriter(r, gz, br),
+	}, nil
+}
+
+func openFileAndVariants(filename string) (io.WriteCloser, error) {
+	if compressFiles {
+		return multiOpenFile(filename)
+	} else {
+		return openFileWrite(filename)
+	}
+}
diff --git a/nix/gomod2nix.toml b/nix/gomod2nix.toml
index 0bc3fc9..268262c 100644
--- a/nix/gomod2nix.toml
+++ b/nix/gomod2nix.toml
@@ -20,6 +20,9 @@ schema = 3
   [mod."github.com/adrg/frontmatter"]
     version = "v0.2.0"
     hash = "sha256-WJsVcdCpkIkjqUz5fJOFStZYwQlrcFzQ6+mZatZiimo="
+  [mod."github.com/andybalholm/brotli"]
+    version = "v1.1.0"
+    hash = "sha256-njLViV4v++ZdgOWGWzlvkefuFvA/nkugl3Ta/h1nu/0="
   [mod."github.com/andybalholm/cascadia"]
     version = "v1.3.2"
     hash = "sha256-Nc9SkqJO/ecincVcUBFITy24TMmMGj5o0Q8EgdNhrEk="