package main import ( "bytes" "crypto/sha256" "fmt" "io/fs" "mime" "os" "os/exec" "path/filepath" "time" ) type memoryAsset struct { contentType string content []byte etag string modified time.Time } func buildPackages(root string) (map[string]memoryAsset, error) { output, err := os.MkdirTemp("", "yawn-packages-*") if err != nil { return nil, err } defer os.RemoveAll(output) if err := runBuild(root, "wasm-pack", "build", filepath.Join(root, "core"), "--target", "web", "--out-dir", filepath.Join(output, "pkg"), "--release"); err != nil { return nil, err } if err := runBuild(root, "node", filepath.Join(root, "server", "build.mjs"), root, output); err != nil { return nil, err } assets := make(map[string]memoryAsset) for _, name := range []string{ "core.js", "handles.js", "worker.js", "importer-worker.js", "bvh-worker.js", } { asset, err := loadMemoryAsset(filepath.Join(output, name)) if err != nil { return nil, err } assets[name] = asset } err = filepath.WalkDir(filepath.Join(output, "pkg"), func(path string, entry fs.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if entry.IsDir() { return nil } relative, err := filepath.Rel(filepath.Join(output, "pkg"), path) if err != nil { return err } asset, err := loadMemoryAsset(path) if err != nil { return err } assets[filepath.ToSlash(filepath.Join("pkg", relative))] = asset return nil }) if err != nil { return nil, err } return assets, nil } func runBuild(directory, name string, arguments ...string) error { command := exec.Command(name, arguments...) command.Dir = directory command.Env = os.Environ() var output bytes.Buffer command.Stdout = &output command.Stderr = &output if err := command.Run(); err != nil { return fmt.Errorf("%s: %w\n%s", name, err, output.String()) } return nil } func loadMemoryAsset(path string) (memoryAsset, error) { content, err := os.ReadFile(path) if err != nil { return memoryAsset{}, err } digest := sha256.Sum256(content) contentType := mime.TypeByExtension(filepath.Ext(path)) if contentType == "" { contentType = "application/octet-stream" } return memoryAsset{ contentType: contentType, content: content, etag: fmt.Sprintf("\"%x\"", digest[:12]), modified: time.Now().UTC(), }, nil }