Files
gowebbuild/config_test.go
pk 49c88da564 Add config extends and entry point exclusion
Profiles may inherit from another config file (extends: <path>): the
first profile of the referenced file is merged underneath (scalars from
the overlay win, slices concatenate base-first and de-duplicate, chains
resolve recursively, cycles are detected).

esbuild.excludeEntryPoints removes merged entry points by glob (** spans
path separators, * stays within a segment), applied after path
resolution so patterns and entries match in the same shape.

Lets product editions/flavors (e.g. the wmd4x Range Trader Pro edition)
build only their own entry set on top of a shared, merge-clean base
config. Includes unit tests and template docs.
2026-10-11 15:58:56 +02:00

157 lines
4.1 KiB
Go

package main
import (
"os"
"path/filepath"
"reflect"
"regexp"
"testing"
)
func TestMergeOptions(t *testing.T) {
base := options{}
base.ESBuild.EntryPoints = []string{"a.js", "b.js"}
base.ESBuild.LogLevel = 3
base.ESBuild.Splitting = true
base.Copy = []struct {
Src string `yaml:"src"`
Dest string `yaml:"dest"`
}{{Src: "index.html", Dest: "dist"}}
overlay := options{}
overlay.ESBuild.EntryPoints = []string{"b.js", "c.js"} // b.js duplicates the base
overlay.ESBuild.LogLevel = 2 // scalar override
overlay.Copy = []struct {
Src string `yaml:"src"`
Dest string `yaml:"dest"`
}{{Src: "index.html", Dest: "dist"}} // exact duplicate of the base entry
merged := mergeOptions(base, overlay)
want := []string{"a.js", "b.js", "c.js"}
if !reflect.DeepEqual(merged.ESBuild.EntryPoints, want) {
t.Fatalf("entryPoints = %v, want %v", merged.ESBuild.EntryPoints, want)
}
if merged.ESBuild.LogLevel != 2 {
t.Fatalf("logLevel = %d, want overlay value 2", merged.ESBuild.LogLevel)
}
if !merged.ESBuild.Splitting {
t.Fatal("splitting = false, want inherited true")
}
if len(merged.Copy) != 1 {
t.Fatalf("copy = %d entries, want de-duplicated 1", len(merged.Copy))
}
if merged.Extends != "" {
t.Fatalf("extends = %q, want cleared after merge", merged.Extends)
}
}
func TestPatternToRegexp(t *testing.T) {
cases := []struct {
pattern string
path string
match bool
}{
// Production shape: processPaths absolutizes patterns and entries
// alike, so matching happens between absolute forward-slash paths.
{"/proj/apps/taxes/**", "/proj/apps/taxes/taxes-app.js", true},
{"/proj/apps/taxes/**", "/proj/apps/taxes/elements/tax-script.js", true},
{"/proj/apps/taxes/**", "/proj/apps/journal/journal-app.js", false},
{"/proj/apps/vault/**", "/proj/apps/vaultx/vaultx-app.js", false},
{"/proj/src/*.js", "/proj/src/the-app.js", true},
{"/proj/src/*.js", "/proj/src/admin/the-admin.js", false},
{"/proj/src/the-?.js", "/proj/src/the-a.js", true},
{"/proj/src/the-?.js", "/proj/src/the-ab.js", false},
}
for _, c := range cases {
re := regexp.MustCompile(patternToRegexp(c.pattern))
if re.MatchString(c.path) != c.match {
t.Errorf("pattern %q vs %q: got %v, want %v", c.pattern, c.path, re.MatchString(c.path), c.match)
}
}
}
func TestFilterEntryPoints(t *testing.T) {
opts := options{}
opts.ESBuild.EntryPoints = []string{
"/proj/apps/taxes/taxes-app.js",
"/proj/apps/chat/chat-app.js",
"/proj/the-terminal.js",
}
opts.ESBuild.ExcludeEntryPoints = []string{"/proj/apps/taxes/**"}
filterEntryPoints(&opts)
want := []string{"/proj/apps/chat/chat-app.js", "/proj/the-terminal.js"}
if !reflect.DeepEqual(opts.ESBuild.EntryPoints, want) {
t.Fatalf("entryPoints = %v, want %v", opts.ESBuild.EntryPoints, want)
}
}
func TestReadCfgExtends(t *testing.T) {
dir := t.TempDir()
prevWd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.Chdir(prevWd) })
writeFile := func(name, content string) {
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0644); err != nil {
t.Fatal(err)
}
}
writeFile("base.yaml", `- esbuild:
entryPoints:
- ./src/base-one.js
- ./src/base-two.js
outdir: ./dist
logLevel: 3
copy:
- src: ./index.html
dest: ./dist
`)
writeFile("overlay.yaml", `- extends: ./base.yaml
esbuild:
entryPoints:
- ./src/base-two.js
- ./src/overlay.js
excludeEntryPoints:
- ./src/base-one.js
`)
if err := os.Chdir(dir); err != nil {
t.Fatal(err)
}
profiles := readCfg("./overlay.yaml")
if len(profiles) != 1 {
t.Fatalf("profiles = %d, want 1", len(profiles))
}
p := profiles[0]
want := []string{
filepath.Join(dir, "src/base-two.js"),
filepath.Join(dir, "src/overlay.js"),
}
if !reflect.DeepEqual(p.ESBuild.EntryPoints, want) {
t.Fatalf("entryPoints = %v, want %v", p.ESBuild.EntryPoints, want)
}
if p.ESBuild.LogLevel != 3 {
t.Fatalf("logLevel = %d, want inherited 3", p.ESBuild.LogLevel)
}
if len(p.Copy) != 1 {
t.Fatalf("copy = %d entries, want inherited 1", len(p.Copy))
}
}