feat(phase-bc): TOML plugin manifests + Solid vitest preset

Plugin TOML support (-tags wazero):
- manifest_toml.go: tryLoadPluginTOML using BurntSushi/toml v1.6.0
- manifest_nottoml.go: no-op stub for default build
- loader.go: prefers plugin.toml, falls back to plugin.json

Solid vitest preset:
- vitest.config.ts: add vite-plugin-solid, expand include to
  src/components/solid/**/*.test.tsx — Badge.test.tsx now runs
  automatically as part of npm test (112 files / 3188 tests)
This commit is contained in:
J3vb
2026-04-06 23:25:16 +02:00
parent ddaaadcd43
commit ae496082b3
7 changed files with 91 additions and 18 deletions
+20 -10
View File
@@ -49,17 +49,27 @@ func scanPluginDirectory(dir string) ([]foundPlugin, error) {
continue
}
pluginDir := filepath.Join(dir, e.Name())
manifestPath := filepath.Join(pluginDir, "plugin.json")
raw, rdErr := os.ReadFile(manifestPath)
if rdErr != nil {
if os.IsNotExist(rdErr) {
continue
}
return nil, fmt.Errorf("plugin %q: read plugin.json: %w", e.Name(), rdErr)
// Prefer plugin.toml (wazero build) over plugin.json.
manifest, ok, tomlErr := tryLoadPluginTOML(pluginDir)
if tomlErr != nil {
return nil, fmt.Errorf("plugin %q: %w", e.Name(), tomlErr)
}
manifest, parseErr := ParseManifest(raw)
if parseErr != nil {
return nil, fmt.Errorf("plugin %q: %w", e.Name(), parseErr)
if !ok {
// Fall back to plugin.json.
manifestPath := filepath.Join(pluginDir, "plugin.json")
raw, rdErr := os.ReadFile(manifestPath)
if rdErr != nil {
if os.IsNotExist(rdErr) {
continue
}
return nil, fmt.Errorf("plugin %q: read plugin.json: %w", e.Name(), rdErr)
}
var parseErr error
manifest, parseErr = ParseManifest(raw)
if parseErr != nil {
return nil, fmt.Errorf("plugin %q: %w", e.Name(), parseErr)
}
}
// Reject any symlinks anywhere in the plugin directory tree. The asset
// handler enforces that resolved paths stay rooted at pluginDir, but
+10
View File
@@ -0,0 +1,10 @@
//go:build !wazero
// Default build stub — TOML manifest parsing is not compiled in without -tags wazero.
package plugin
// tryLoadPluginTOML always reports "not present" in the default build so the
// loader unconditionally falls through to plugin.json.
func tryLoadPluginTOML(_ string) (*Manifest, bool, error) {
return nil, false, nil
}
+39
View File
@@ -0,0 +1,39 @@
//go:build wazero
// TOML manifest support — compiled only with -tags wazero.
// Plugins may ship either plugin.json or plugin.toml; this file provides
// tryLoadPluginTOML which the loader calls before falling back to JSON.
package plugin
import (
"fmt"
"os"
"path/filepath"
"github.com/BurntSushi/toml"
)
// tryLoadPluginTOML attempts to parse a plugin.toml from pluginDir.
// Returns the parsed Manifest and true on success.
// Returns nil, false if no plugin.toml exists (caller should try plugin.json).
// Returns nil, false plus logs if plugin.toml exists but is malformed — the
// caller will skip the plugin and log the error.
func tryLoadPluginTOML(pluginDir string) (*Manifest, bool, error) {
tomlPath := filepath.Join(pluginDir, "plugin.toml")
raw, err := os.ReadFile(tomlPath)
if os.IsNotExist(err) {
return nil, false, nil // not present; try JSON
}
if err != nil {
return nil, false, fmt.Errorf("read plugin.toml: %w", err)
}
var m Manifest
if _, err := toml.Decode(string(raw), &m); err != nil {
return nil, false, fmt.Errorf("parse plugin.toml: %w", err)
}
if err := m.Validate(); err != nil {
return nil, false, fmt.Errorf("invalid plugin.toml: %w", err)
}
return &m, true, nil
}