mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-03 03:27:10 +00:00
Merge branch 'spec-npm-release' into pr-73-followups
# Conflicts: # Makefile
This commit is contained in:
commit
b2877a97b1
11 files changed
+207
-74
No files matched your search
@@ -350,16 +350,20 @@ func resolveRuntimeSibling(specAPIPath, userSpecPath, filename string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// resolveSpecAPIPath returns the path to pkg/spec/src/index.ts inside
|
||||
// a sanderling source checkout, searched upward from the spec file and the cwd.
|
||||
// Returns "" when not found, in which case esbuild resolves @sanderling/spec via
|
||||
// node_modules the way a downstream user's project would.
|
||||
// resolveSpecAPIPath returns the path to the spec API's index.ts: a sanderling
|
||||
// source checkout first, searched upward from the spec file and the cwd, then
|
||||
// an installed node_modules/@sanderling/spec. Aliasing the installed copy is
|
||||
// what keeps the spec and the runtime entry on one module graph; resolving the
|
||||
// bare specifier through package.json "exports" would load dist/ alongside the
|
||||
// runtime's src/ and give sampler-rng.ts two instances.
|
||||
func resolveSpecAPIPath(specPath string) string {
|
||||
var candidates []string
|
||||
var checkout, installed []string
|
||||
if absoluteSpec, err := filepath.Abs(specPath); err == nil {
|
||||
directory := filepath.Dir(absoluteSpec)
|
||||
for {
|
||||
candidates = append(candidates, filepath.Join(directory, "pkg/spec/src/index.ts"))
|
||||
checkout = append(checkout, filepath.Join(directory, "pkg/spec/src/index.ts"))
|
||||
installed = append(installed,
|
||||
filepath.Join(directory, "node_modules/@sanderling/spec/src/index.ts"))
|
||||
parent := filepath.Dir(directory)
|
||||
if parent == directory {
|
||||
break
|
||||
@@ -368,9 +372,9 @@ func resolveSpecAPIPath(specPath string) string {
|
||||
}
|
||||
}
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
candidates = append(candidates, filepath.Join(cwd, "pkg/spec/src/index.ts"))
|
||||
checkout = append(checkout, filepath.Join(cwd, "pkg/spec/src/index.ts"))
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
for _, candidate := range append(checkout, installed...) {
|
||||
if _, err := os.Stat(candidate); err == nil {
|
||||
return candidate
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package testrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -304,3 +306,153 @@ func TestLaunchAppBoundsWedgedDriver(t *testing.T) {
|
||||
t.Fatal("launchApp never returned: the pre-run launch is unbounded, so a wedged driver hangs the run forever")
|
||||
}
|
||||
}
|
||||
|
||||
// repoFile walks up from the test's working directory and returns the absolute
|
||||
// path of rel inside the sanderling checkout.
|
||||
func repoFile(t *testing.T, rel string) string {
|
||||
t.Helper()
|
||||
directory, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for {
|
||||
candidate := filepath.Join(directory, rel)
|
||||
if _, err := os.Stat(candidate); err == nil {
|
||||
return candidate
|
||||
}
|
||||
parent := filepath.Dir(directory)
|
||||
if parent == directory {
|
||||
t.Fatalf("%s not found above the test directory", rel)
|
||||
}
|
||||
directory = parent
|
||||
}
|
||||
}
|
||||
|
||||
// publishedFiles returns the "files" entries of pkg/spec/package.json, the
|
||||
// exact set npm ships in the @sanderling/spec tarball.
|
||||
func publishedFiles(t *testing.T) []string {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile(repoFile(t, "pkg/spec/package.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var manifest struct {
|
||||
Files []string `json:"files"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &manifest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return manifest.Files
|
||||
}
|
||||
|
||||
// installPublishedPackage reproduces what `npm install @sanderling/spec`
|
||||
// unpacks into node_modules: only the paths package.json publishes.
|
||||
func installPublishedPackage(t *testing.T, dest string) {
|
||||
t.Helper()
|
||||
specDir := filepath.Dir(repoFile(t, "pkg/spec/package.json"))
|
||||
for _, entry := range publishedFiles(t) {
|
||||
source := filepath.Join(specDir, entry)
|
||||
if _, err := os.Stat(source); err != nil {
|
||||
continue
|
||||
}
|
||||
copyTree(t, source, filepath.Join(dest, entry))
|
||||
}
|
||||
}
|
||||
|
||||
func copyTree(t *testing.T, source, dest string) {
|
||||
t.Helper()
|
||||
err := filepath.WalkDir(source, func(path string, entry fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relative, err := filepath.Rel(source, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target := filepath.Join(dest, relative)
|
||||
if entry.IsDir() {
|
||||
return os.MkdirAll(target, 0o755)
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(target, data, 0o644)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveRuntimeSibling_PublishedPackageShipsTheRuntimes pins npm's "files"
|
||||
// list against the resolver that consumes it. The tarball shipped dist/ alone
|
||||
// while the node_modules fallback looks for src/goja-runtime.ts, so every
|
||||
// `npm install @sanderling/spec` user hit "goja-runtime.ts not found".
|
||||
func TestResolveRuntimeSibling_PublishedPackageShipsTheRuntimes(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
installPublishedPackage(t, filepath.Join(root, "node_modules", "@sanderling", "spec"))
|
||||
specPath := filepath.Join(root, "spec.ts")
|
||||
if err := os.WriteFile(specPath, []byte(""), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, filename := range []string{"goja-runtime.ts", "web-runtime.ts"} {
|
||||
if resolveRuntimeSibling("", specPath, filename) == "" {
|
||||
t.Errorf("%s unreachable from a published install; package.json publishes %v",
|
||||
filename, publishedFiles(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrepareBundleInputs_InstalledPackageSharesOneModuleGraph pins the
|
||||
// downstream case: with no sanderling checkout above the spec, the aliases and
|
||||
// the runtime entry must name the SAME installed copy. An unset alias let
|
||||
// esbuild resolve @sanderling/spec to dist/ while the runtime came from src/,
|
||||
// which loads sampler-rng.ts twice; from(), strings(), integers() and emails()
|
||||
// then read an rng the picker never set and collapse to a fixed default.
|
||||
func TestPrepareBundleInputs_InstalledPackageSharesOneModuleGraph(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
installed := filepath.Join(root, "node_modules", "@sanderling", "spec")
|
||||
installPublishedPackage(t, installed)
|
||||
specPath := filepath.Join(root, "sanderling", "spec.ts")
|
||||
if err := os.MkdirAll(filepath.Dir(specPath), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(specPath, []byte(""), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = os.Chdir(cwd) })
|
||||
if err := os.Chdir(root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
prep, err := prepareBundleInputs(Options{Spec: specPath})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source := filepath.Join(installed, "src")
|
||||
want := map[string]string{
|
||||
"@sanderling/spec": filepath.Join(source, "index.ts"),
|
||||
"@sanderling/spec/defaults": filepath.Join(source, "defaults/index.ts"),
|
||||
"@sanderling/spec/defaults/properties": filepath.Join(source, "defaults/properties.ts"),
|
||||
}
|
||||
for key, wantValue := range want {
|
||||
if prep.aliases[key] != wantValue {
|
||||
t.Errorf("alias %q = %q, want %q", key, prep.aliases[key], wantValue)
|
||||
}
|
||||
}
|
||||
if got := prep.gojaRuntimePath; got != filepath.Join(source, "goja-runtime.ts") {
|
||||
t.Errorf("gojaRuntimePath = %q, want it beside the aliased index.ts", got)
|
||||
}
|
||||
if got := resolveWebRuntimePath(prep.specAPIPath, specPath); got != filepath.Join(source, "web-runtime.ts") {
|
||||
t.Errorf("webRuntimePath = %q, want it beside the aliased index.ts", got)
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ type stateInput struct {
|
||||
}
|
||||
|
||||
// stateObject builds the JS-side `state` object matching the State type from
|
||||
// pkg/spec-api. Fields beyond snapshots/ax are included when the caller
|
||||
// pkg/spec. Fields beyond snapshots/ax are included when the caller
|
||||
// populated them on stateInput.
|
||||
func stateObject(runtime *goja.Runtime, input stateInput) (*goja.Object, error) {
|
||||
state := runtime.NewObject()
|
||||
|
||||
Reference in new issue
Block a user