summaryrefslogtreecommitdiff
path: root/runner/js
diff options
context:
space:
mode:
authorewy <ewy0@protonmail.com>2026-05-02 18:28:40 +0200
committerewy <ewy0@protonmail.com>2026-05-02 18:28:40 +0200
commite8b9ca135642c76000bc0ec2e78674c602a748dd (patch)
tree4e502ce767a4a4f70e40d6e72a3817bc139f1300 /runner/js
parent35e6ac334f6a62cf63b743182cd5355f6baf4406 (diff)
add .js and .ts support
also: remove unused/duplicate method runner.Hydrate
Diffstat (limited to 'runner/js')
-rw-r--r--runner/js/indexer.go39
-rw-r--r--runner/js/js.go106
-rw-r--r--runner/js/runner.go28
-rw-r--r--runner/js/target_npm.go42
-rw-r--r--runner/js/target_script.go55
5 files changed, 270 insertions, 0 deletions
diff --git a/runner/js/indexer.go b/runner/js/indexer.go
new file mode 100644
index 0000000..8a2a6b2
--- /dev/null
+++ b/runner/js/indexer.go
@@ -0,0 +1,39 @@
+package js
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/ewy1/pik/model"
+ "io/fs"
+ "os"
+ "path/filepath"
+)
+
+type Package struct {
+ Scripts map[string]string `json:"scripts"`
+}
+
+func (n *js) Index(path string, f fs.FS, runners []model.Runner) ([]model.Target, error) {
+ p := &Package{}
+ // are there any other package.jsons? i hope not, because i don't know them
+ content, err := os.ReadFile(filepath.Join(path, "package.json"))
+ if errors.Is(err, fs.ErrNotExist) {
+ return nil, nil
+ } else if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(content, p)
+ if err != nil {
+ return nil, err
+ }
+
+ var targets []model.Target
+ if n.Npm == "" {
+ return nil, NoNpm
+ }
+ for k, s := range p.Scripts {
+ targets = append(targets, n.CreateRun(k, s))
+ }
+
+ return targets, nil
+}
diff --git a/runner/js/js.go b/runner/js/js.go
new file mode 100644
index 0000000..7c2af58
--- /dev/null
+++ b/runner/js/js.go
@@ -0,0 +1,106 @@
+package js
+
+import (
+ "errors"
+ "github.com/ewy1/pik/identity"
+ "github.com/ewy1/pik/model"
+ "github.com/ewy1/pik/runner"
+ "os/exec"
+ "path/filepath"
+ "slices"
+)
+
+var jsExtensions = []string{
+ ".js",
+ ".cjs",
+}
+
+var tsExtensions = []string{
+ ".ts",
+}
+
+var extensions = append(jsExtensions, tsExtensions...)
+
+var managers = []string{
+ "pnpm",
+ "yarn",
+ "npm",
+}
+
+var jsInterpreters = []string{
+ "node",
+ "bun",
+}
+
+var tsInterpeters = []string{
+ "ts",
+ "ts-node",
+ "bun",
+}
+
+type js struct {
+ JsInterpreter string
+ TsInterpreter string
+ Npm string
+}
+
+var Js = &js{}
+
+var UnsupportedFile = errors.New("unsupported file")
+var NoJsInterpreter = errors.New("no js interpreter found in $PATH")
+var NoTsInterpreter = errors.New("no ts interpreter found in $PATH")
+var NoNpm = errors.New("npm not found in $PATH")
+
+func (n *js) Interpreter(file string) (string, error) {
+ ext := filepath.Ext(file)
+ if slices.Contains(jsInterpreters, ext) {
+ if n.JsInterpreter == "" {
+ return "", NoJsInterpreter
+ }
+ return n.JsInterpreter, nil
+ }
+ if slices.Contains(tsInterpeters, ext) {
+ if n.TsInterpreter == "" {
+ return "", NoTsInterpreter
+ }
+ return n.TsInterpreter, nil
+ }
+ return "", UnsupportedFile
+}
+
+func (n *js) Init() error {
+ for _, p := range jsInterpreters {
+ if r, err := exec.LookPath(p); err != nil {
+ n.JsInterpreter = r
+ }
+ }
+ for _, p := range tsInterpeters {
+ if r, err := exec.LookPath(p); err != nil {
+ n.TsInterpreter = r
+ }
+ }
+
+ for _, m := range managers {
+ if r, err := exec.LookPath(m); err == nil {
+ n.Npm = r
+ }
+ }
+
+ return nil
+}
+
+var npmSub = []string{
+ "npm",
+}
+
+func (n *js) CreateRun(name, cmd string) model.Target {
+ return &Npm{
+ BaseTarget: runner.BaseTarget{
+ Identity: identity.New(cmd),
+ MyTags: model.TagsFromFilename(cmd),
+ MySub: npmSub,
+ },
+ Name: name,
+ Cmd: cmd,
+ }
+}
diff --git a/runner/js/runner.go b/runner/js/runner.go
new file mode 100644
index 0000000..8b5f4f1
--- /dev/null
+++ b/runner/js/runner.go
@@ -0,0 +1,28 @@
+package js
+
+import (
+ "github.com/ewy1/pik/identity"
+ "github.com/ewy1/pik/model"
+ "github.com/ewy1/pik/runner"
+ "io/fs"
+ "path/filepath"
+ "slices"
+)
+
+func (n *js) Wants(fs fs.FS, file string, entry fs.DirEntry) (bool, error) {
+ ext := filepath.Ext(entry.Name())
+ return slices.Contains(extensions, ext), nil
+}
+
+func (n *js) CreateTarget(fs fs.FS, source string, file string, entry fs.DirEntry) (model.Target, error) {
+ ext := filepath.Ext(entry.Name())
+ typed := slices.Contains(tsExtensions, ext)
+ return &Script{
+ BaseTarget: runner.BaseTarget{
+ Identity: identity.New(entry.Name()),
+ MyTags: model.TagsFromFilename(entry.Name()),
+ MySub: runner.SubFromFile(file),
+ },
+ Typed: typed,
+ }, nil
+}
diff --git a/runner/js/target_npm.go b/runner/js/target_npm.go
new file mode 100644
index 0000000..8319fad
--- /dev/null
+++ b/runner/js/target_npm.go
@@ -0,0 +1,42 @@
+package js
+
+import (
+ "github.com/ewy1/pik/model"
+ "github.com/ewy1/pik/runner"
+ "os/exec"
+ "path/filepath"
+)
+
+type Npm struct {
+ runner.BaseTarget
+ Name string
+ Cmd string
+}
+
+func (n *Npm) Icon() string {
+ return "\uE60B"
+}
+
+func (n *Npm) Description(src *model.HydratedSource) string {
+ return n.Cmd
+}
+
+func (n *Npm) Target() model.Target {
+ return n
+}
+
+func (n *Npm) Create(s *model.Source) *exec.Cmd {
+ return exec.Command(Js.Npm, "run", n.Name)
+}
+
+func (n *Npm) Label() string {
+ return n.Name
+}
+
+func (n *Npm) Hydrate(src *model.Source) (model.HydratedTarget, error) {
+ return n, nil
+}
+
+func (n *Npm) File(src *model.Source) string {
+ return filepath.Join(src.Path, "package.json")
+}
diff --git a/runner/js/target_script.go b/runner/js/target_script.go
new file mode 100644
index 0000000..6ad213d
--- /dev/null
+++ b/runner/js/target_script.go
@@ -0,0 +1,55 @@
+package js
+
+import (
+ "github.com/ewy1/pik/describe"
+ "github.com/ewy1/pik/model"
+ "github.com/ewy1/pik/runner"
+ "github.com/ewy1/pik/spool"
+ "os/exec"
+ "path/filepath"
+)
+
+type Script struct {
+ runner.BaseTarget
+ Typed bool
+}
+
+func (t *Script) Icon() string {
+ if t.Typed {
+ return "\uE628"
+ } else {
+ return "\uE60C"
+ }
+}
+
+func (t *Script) Description(src *model.HydratedSource) string {
+ d, err := describe.Describe(t, t.File(src.Source))
+ if err != nil {
+ _, _ = spool.Warn("%v\n", err)
+ }
+ return d
+}
+
+func (t *Script) Target() model.Target {
+ return t
+}
+
+func (t *Script) Create(s *model.Source) *exec.Cmd {
+ if t.Typed {
+ return exec.Command(Js.TsInterpreter, t.File(s))
+ } else {
+ return exec.Command(Js.JsInterpreter, t.File(s))
+ }
+}
+
+func (t *Script) Label() string {
+ return t.Identity.Full
+}
+
+func (t *Script) Hydrate(src *model.Source) (model.HydratedTarget, error) {
+ return t, nil
+}
+
+func (t *Script) File(src *model.Source) string {
+ return filepath.Join(src.Path, "package.json")
+}