feat(bundle-check): fail a spec that bundles but registers no properties

This commit is contained in:
pj committed 2026-08-17 23:27:29 +05:30
1 parent 9523133c59
commit 26455d5ec4
1 file changed
+52 -5
+52 -5
View File
@@ -1,14 +1,24 @@
// Command bundle-check is a developer tool that bundles a spec file to confirm it compiles. // Command bundle-check is a developer tool that bundles a spec file and loads
// it into the evaluator to confirm it compiles and registers properties.
package main package main
import ( import (
"errors"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/priyanshujain/sanderling/internal/bundler" "github.com/priyanshujain/sanderling/internal/bundler"
"github.com/priyanshujain/sanderling/internal/testrun"
"github.com/priyanshujain/sanderling/internal/verifier"
) )
// checkSeed keeps the load deterministic. The bundle it seeds is only loaded,
// never hashed or reported, so the value is arbitrary.
const checkSeed = 1
func bundleSpec(specSrc, entryFile string) (bundler.Result, error) { func bundleSpec(specSrc, entryFile string) (bundler.Result, error) {
return bundler.Bundle(bundler.Options{ return bundler.Bundle(bundler.Options{
EntryFile: entryFile, EntryFile: entryFile,
@@ -20,6 +30,45 @@ func bundleSpec(specSrc, entryFile string) (bundler.Result, error) {
}) })
} }
// registeredProperties bundles the spec the way a run bundles it, with the
// runtime entry that assigns globalThis.properties, and loads it into the real
// evaluator. Bundling alone proves nothing about registration: a spec that
// registers no property compiles perfectly and then judges nothing.
func registeredProperties(entryFile string) ([]string, error) {
bundle, err := testrun.BundleSpec(entryFile, checkSeed)
if err != nil {
return nil, fmt.Errorf("bundle with runtime entry: %w", err)
}
evaluator, err := verifier.New()
if err != nil {
return nil, fmt.Errorf("evaluator: %w", err)
}
if err := evaluator.Load(string(bundle.JavaScript)); err != nil {
return nil, fmt.Errorf("load spec: %w", err)
}
return evaluator.PropertyNames(), nil
}
func check(specSrc, entryFile string, stdout io.Writer) error {
result, err := bundleSpec(specSrc, entryFile)
if err != nil {
return fmt.Errorf("bundle: %w", err)
}
fmt.Fprintf(stdout, "bundled: %d bytes, sha256=%s\n", len(result.JavaScript), result.SHA256)
names, err := registeredProperties(entryFile)
if err != nil {
return err
}
if len(names) == 0 {
return errors.New("the spec bundles and loads cleanly but registers no properties: " +
"nothing is wrong with the source, and a run against it would check nothing " +
"and report no violations")
}
fmt.Fprintf(stdout, "properties registered: %d (%s)\n", len(names), strings.Join(names, ", "))
return nil
}
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: bundle-check <spec.ts>") fmt.Fprintln(os.Stderr, "usage: bundle-check <spec.ts>")
@@ -38,10 +87,8 @@ func main() {
os.Exit(1) os.Exit(1)
} }
result, err := bundleSpec(filepath.Join(repoRoot, "pkg/spec/src"), entryFile) if err := check(filepath.Join(repoRoot, "pkg/spec/src"), entryFile, os.Stdout); err != nil {
if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err)
fmt.Fprintf(os.Stderr, "bundle: %v\n", err)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("bundled: %d bytes, sha256=%s\n", len(result.JavaScript), result.SHA256)
} }