buildOptions struct

Fields:

  • Devtools (bool)
  • DevBuild (bool)
  • SkipOptimize (bool)

goBuildArgs function

Parameters:

  • opts buildOptions

Returns:

  • []string

References:

Show/Hide Function Body
{
	args := []string{"build"}
	var tags []string
	if opts.Devtools {
		tags = append(tags, "devtools")
	}
	if opts.DevBuild {
		tags = append(tags, "rfwdev")
	}
	if len(tags) > 0 {
		args = append(args, "-tags="+strings.Join(tags, ","))
	}
	if !opts.SkipOptimize {
		args = append(args, "-trimpath", "-ldflags=-s -w")
	}
	return args
}

Build function

Returns:

  • error
Show/Hide Function Body
{
	var manifest struct {
		Build struct {
			Type string `json:"type"`
		} `json:"build"`
		Plugins map[string]json.RawMessage `json:"plugins"`
	}
	if data, err := os.ReadFile("rfw.json"); err == nil {
		_ = json.Unmarshal(data, &manifest)
	}
	if os.Getenv("RFW_DEVTOOLS") == "1" {
		if manifest.Plugins == nil {
			manifest.Plugins = map[string]json.RawMessage{}
		}
		manifest.Plugins["devtools"] = nil
	}
	if err := plugins.Configure(manifest.Plugins); err != nil {
		return fmt.Errorf("failed to configure plugins: %w", err)
	}
	if err := plugins.PreBuild(); err != nil {
		return fmt.Errorf("pre build failed: %w", err)
	}

	clientDir := filepath.Join("build", "client")
	hostDir := filepath.Join("build", "host")
	staticDir := filepath.Join("build", "static")
	if err := os.MkdirAll(clientDir, 0o755); err != nil {
		return fmt.Errorf("failed to create client build directory: %w", err)
	}
	if err := os.MkdirAll(staticDir, 0o755); err != nil {
		return fmt.Errorf("failed to create static build directory: %w", err)
	}

	goroot, err := exec.Command("go", "env", "GOROOT").Output()
	if err != nil {
		return fmt.Errorf("failed to get GOROOT: %w", err)
	}
	wasmExec := filepath.Join(strings.TrimSpace(string(goroot)), "lib", "wasm", "wasm_exec.js")
	if err := copyFile(wasmExec, filepath.Join(clientDir, "wasm_exec.js")); err != nil {
		return fmt.Errorf("failed to copy wasm_exec.js: %w", err)
	}

	args := goBuildArgs(buildOptions{
		Devtools:     os.Getenv("RFW_DEVTOOLS") == "1",
		DevBuild:     os.Getenv("RFW_DEV_BUILD") == "1",
		SkipOptimize: utils.IsDebug() || os.Getenv("RFW_SKIP_STRIP") == "1",
	})
	wasmPath := filepath.Join(clientDir, "app.wasm")
	args = append(args, "-o", wasmPath, ".")
	cmd := exec.Command("go", args...)
	cmd.Env = append(os.Environ(), "GOARCH=wasm", "GOOS=js")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to build project: %s: %w", output, err)
	}

	if err := compressWasmBrotli(wasmPath); err != nil {
		return fmt.Errorf("failed to brotli-compress wasm: %w", err)
	}

	if err := os.MkdirAll(hostDir, 0o755); err != nil {
		return fmt.Errorf("failed to create host build directory: %w", err)
	}
	hostCmd := exec.Command("go", "build", "-o", filepath.Join(hostDir, "host"), "./host")
	if hostOutput, err := hostCmd.CombinedOutput(); err != nil {
		return fmt.Errorf("failed to build host components: %s: %w", hostOutput, err)
	}
	if err := plugins.Build(); err != nil {
		return fmt.Errorf("failed to run plugins: %w", err)
	}
	if _, err := os.Stat("index.html"); err == nil {
		if err := copyFile("index.html", filepath.Join(clientDir, "index.html")); err != nil {
			return fmt.Errorf("failed to copy index.html: %w", err)
		}
	}

	if _, err := os.Stat("wasm_loader.js"); err == nil {
		if err := copyFile("wasm_loader.js", filepath.Join(clientDir, "wasm_loader.js")); err != nil {
			return fmt.Errorf("failed to copy wasm_loader.js: %w", err)
		}
	}

	if _, err := os.Stat("static"); err == nil {
		if err := filepath.Walk("static", func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}
			rel, err := filepath.Rel("static", path)
			if err != nil {
				return err
			}
			dst := filepath.Join(staticDir, rel)
			if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
				return err
			}
			return copyFile(path, dst)
		}); err != nil {
			return fmt.Errorf("failed to copy static assets: %w", err)
		}
	}

	if err := plugins.PostBuild(); err != nil {
		return fmt.Errorf("post build failed: %w", err)
	}

	return nil
}

copyFile function

Parameters:

  • src string
  • dst string

Returns:

  • error
Show/Hide Function Body
{
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	if _, err = io.Copy(out, in); err != nil {
		return err
	}
	return out.Close()
}

compressWasmBrotli function

Parameters:

  • src string

Returns:

  • err error
Show/Hide Function Body
{
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	dst := src + ".br"
	tmp, err := os.CreateTemp(filepath.Dir(dst), filepath.Base(dst)+".*")
	if err != nil {
		return err
	}
	tmpName := tmp.Name()
	defer func() {
		if tmp != nil {
			tmp.Close()
		}
		if err != nil {
			_ = os.Remove(tmpName)
		}
	}()

	writer := brotli.NewWriterLevel(tmp, brotli.BestCompression)
	if _, err := io.Copy(writer, in); err != nil {
		writer.Close()
		return err
	}
	if err := writer.Close(); err != nil {
		return err
	}
	if err := tmp.Close(); err != nil {
		return err
	}
	tmp = nil
	if err := os.Rename(tmpName, dst); err != nil {
		return err
	}
	return nil
}

TestCopyFile function

TestCopyFile ensures copyFile replicates the source file's contents at the

destination path.

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	dir := t.TempDir()
	src := filepath.Join(dir, "src.txt")
	dst := filepath.Join(dir, "dst.txt")
	content := []byte("hello world")
	if err := os.WriteFile(src, content, 0o644); err != nil {
		t.Fatalf("write src: %v", err)
	}

	if err := copyFile(src, dst); err != nil {
		t.Fatalf("copyFile error: %v", err)
	}

	got, err := os.ReadFile(dst)
	if err != nil {
		t.Fatalf("read dst: %v", err)
	}
	if string(got) != string(content) {
		t.Fatalf("expected %q, got %q", content, got)
	}
}

TestCompressWasmBrotli function

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	dir := t.TempDir()
	src := filepath.Join(dir, "app.wasm")
	content := []byte(strings.Repeat("rfw wasm", 32))
	if err := os.WriteFile(src, content, 0o644); err != nil {
		t.Fatalf("write wasm: %v", err)
	}

	if err := compressWasmBrotli(src); err != nil {
		t.Fatalf("compressWasmBrotli: %v", err)
	}

	brPath := src + ".br"
	f, err := os.Open(brPath)
	if err != nil {
		t.Fatalf("open brotli file: %v", err)
	}
	defer f.Close()

	reader := brotli.NewReader(f)
	decompressed, err := io.ReadAll(reader)
	if err != nil {
		t.Fatalf("read brotli: %v", err)
	}
	if string(decompressed) != string(content) {
		t.Fatalf("unexpected decompressed content")
	}
}

encoding/json import

Import example:

import "encoding/json"

fmt import

Import example:

import "fmt"

io import

Import example:

import "io"

os import

Import example:

import "os"

os/exec import

Import example:

import "os/exec"

path/filepath import

Import example:

import "path/filepath"

strings import

Import example:

import "strings"

github.com/andybalholm/brotli import

Import example:

import "github.com/andybalholm/brotli"

github.com/rfwlab/rfw/cmd/rfw/plugins import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins"

github.com/rfwlab/rfw/cmd/rfw/plugins/assets import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/assets"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/bundler import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/bundler"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/copy import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/copy"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/devtools import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/devtools"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/docs import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/docs"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/env import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/env"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/pages import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/pages"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/seo import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/seo"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/tailwind import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/tailwind"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/plugins/test import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/plugins/test"

Imported as:

Anonymous Import

github.com/rfwlab/rfw/cmd/rfw/utils import

Import example:

import "github.com/rfwlab/rfw/cmd/rfw/utils"

io import

Import example:

import "io"

os import

Import example:

import "os"

path/filepath import

Import example:

import "path/filepath"

strings import

Import example:

import "strings"

testing import

Import example:

import "testing"

github.com/andybalholm/brotli import

Import example:

import "github.com/andybalholm/brotli"