pull/300/head
Anton Medvedev 2 months ago
parent b7c4bab9f1
commit 58b646c3f4
No known key found for this signature in database

@ -23,10 +23,10 @@ require (
github.com/aymanbagabas/go-udiff v0.1.3 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect

@ -23,8 +23,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM=
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
@ -40,8 +41,9 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5Nq
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=

@ -1,6 +1,7 @@
package complete
import (
_ "embed"
"fmt"
"os"
"path/filepath"
@ -10,6 +11,7 @@ import (
"github.com/dop251/goja"
"github.com/goccy/go-yaml"
"github.com/antonmedv/fx/internal/engine"
"github.com/antonmedv/fx/internal/shlex"
)
@ -53,6 +55,9 @@ var globals = []string{
"skip",
}
//go:embed prelude.js
var prelude string
func Complete() bool {
compLine, ok := os.LookupEnv("COMP_LINE")
@ -145,7 +150,7 @@ func doComplete(compLine string, compWord string) {
}
}
codeComplete(string(input), args, compWord)
codeComplete(input, args, compWord)
}
}
@ -163,7 +168,7 @@ func globalsComplete(compWord string) bool {
return false
}
func codeComplete(input string, args []string, compWord string) {
func codeComplete(input []byte, args []string, compWord string) {
args = args[2:] // Drop binary & file from the args.
if compWord == "" {
@ -180,21 +185,24 @@ func codeComplete(input string, args []string, compWord string) {
var code strings.Builder
code.WriteString(prelude)
code.WriteString(fmt.Sprintf("let json = %s\n", input))
code.WriteString(engine.Stdlib)
code.WriteString("let json = ")
code.Write(input)
for _, arg := range args {
if arg == "" {
if arg == "" { // After dropTail, we can have empty strings.
continue
}
code.WriteString(Transform(arg))
code.WriteString(engine.Transform(arg))
}
code.WriteString("\n__keys\n")
out, err := goja.New().RunString(code.String())
vm := goja.New()
value, err := vm.RunString(code.String())
if err != nil {
return
}
if array, ok := out.Export().([]interface{}); ok {
if array, ok := value.Export().([]interface{}); ok {
prefix := dropTail(compWord)
var reply []string
for _, key := range array {

@ -0,0 +1,10 @@
const __keys = new Set()
Object.prototype.__keys = function () {
if (Array.isArray(this)) return
if (typeof this === 'string') return
if (this instanceof String) return
if (typeof this === 'object' && this !== null)
Object.keys(this).forEach(x => __keys.add(x))
}

@ -0,0 +1,108 @@
package engine
import (
_ "embed"
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/dop251/goja"
"github.com/goccy/go-yaml"
)
//go:embed stdlib.js
var Stdlib string
//go:embed prelude.js
var prelude string
func Reduce(args []string) {
if len(args) < 1 {
panic("args must have at least one element")
}
var (
flagYaml bool
flagRaw bool
flagSlurp bool
)
var src io.Reader = os.Stdin
if isFile(args[0]) {
src = open(args[0], &flagYaml)
args = args[1:]
} else if isFile(args[len(args)-1]) {
src = open(args[len(args)-1], &flagYaml)
args = args[:len(args)-1]
}
var fns []string
for _, arg := range args {
switch arg {
case "--yaml":
flagYaml = true
case "--raw", "-r":
flagRaw = true
case "--slurp", "-s":
flagSlurp = true
case "-rs", "-sr":
flagRaw = true
flagSlurp = true
default:
fns = append(fns, arg)
}
}
if flagSlurp {
println("Error: Built-in JS engine does not support \"--slurp\" flag. Install Node.js or Deno to use this flag.")
os.Exit(1)
}
data, err := io.ReadAll(src)
if err != nil {
panic(err)
}
if flagRaw {
data = []byte(strconv.Quote(string(data)))
} else if flagYaml {
data, err = yaml.YAMLToJSON(data)
if err != nil {
println(err.Error())
os.Exit(1)
}
} else {
}
var code strings.Builder
code.WriteString(prelude)
code.WriteString(Stdlib)
code.WriteString(fmt.Sprintf("let json = JSON.parse(%q)\n", data))
for _, fn := range fns {
code.WriteString(Transform(fn))
}
code.WriteString("JSON.stringify(json)")
vm := goja.New()
vm.Set("println", func(s string) any {
fmt.Println(s)
return nil
})
value, err := vm.RunString(code.String())
if err != nil {
println(err.Error())
os.Exit(1)
}
output, ok := value.Export().(string)
if !ok {
println("undefined")
return
}
fmt.Println(output)
}

@ -0,0 +1,9 @@
const console = {
log: function (...args) {
const parts = []
for (const arg of args) {
parts.push(typeof arg === 'string' ? arg : JSON.stringify(arg, null, 2))
}
println(parts.join(' '))
},
}

@ -1,16 +1,3 @@
package complete
const prelude = `
const __keys = new Set()
Object.prototype.__keys = function () {
if (Array.isArray(this)) return
if (typeof this === 'string') return
if (this instanceof String) return
if (typeof this === 'object' && this !== null)
Object.keys(this).forEach(x => __keys.add(x))
}
function apply(fn, ...args) {
if (typeof fn === 'function') return fn(...args)
return fn
@ -20,23 +7,23 @@ function len(x) {
if (Array.isArray(x)) return x.length
if (typeof x === 'string') return x.length
if (typeof x === 'object' && x !== null) return Object.keys(x).length
throw new Error()
throw new Error(`Cannot get length of ${typeof x}`)
}
function uniq(x) {
if (Array.isArray(x)) return [...new Set(x)]
throw new Error()
throw new Error(`Cannot get unique values of ${typeof x}`)
}
function sort(x) {
if (Array.isArray(x)) return x.sort()
throw new Error()
throw new Error(`Cannot sort ${typeof x}`)
}
function map(fn) {
return function (x) {
if (Array.isArray(x)) return x.map((v, i) => fn(v, i))
throw new Error()
throw new Error(`Cannot map ${typeof x}`)
}
}
@ -47,7 +34,7 @@ function sortBy(fn) {
const fb = fn(b)
return fa < fb ? -1 : fa > fb ? 1 : 0
})
throw new Error()
throw new Error(`Cannot sort ${typeof x}`)
}
}
@ -85,21 +72,20 @@ function zip(...x) {
function flatten(x) {
if (Array.isArray(x)) return x.flat()
throw new Error()
throw new Error(`Cannot flatten ${typeof x}`)
}
function reverse(x) {
if (Array.isArray(x)) return x.reverse()
throw new Error()
throw new Error(`Cannot reverse ${typeof x}`)
}
function keys(x) {
if (typeof x === 'object' && x !== null) return Object.keys(x)
throw new Error()
throw new Error(`Cannot get keys of ${typeof x}`)
}
function values(x) {
if (typeof x === 'object' && x !== null) return Object.values(x)
throw new Error()
throw new Error(`Cannot get values of ${typeof x}`)
}
`

@ -1,4 +1,4 @@
package complete
package engine
import (
"fmt"

@ -0,0 +1,36 @@
package engine
import (
"errors"
"io/fs"
"os"
"path"
"regexp"
)
func isFile(name string) bool {
stat, err := os.Stat(name)
if err != nil {
return false
}
return !stat.IsDir()
}
func open(filePath string, flagYaml *bool) *os.File {
f, err := os.Open(filePath)
if err != nil {
var pathError *fs.PathError
if errors.As(err, &pathError) {
println(err.Error())
os.Exit(1)
} else {
panic(err)
}
}
fileName := path.Base(filePath)
hasYamlExt, _ := regexp.MatchString(`(?i)\.ya?ml$`, fileName)
if !*flagYaml && hasYamlExt {
*flagYaml = true
}
return f
}

@ -6,6 +6,8 @@ import (
"os"
"os/exec"
"path"
"github.com/antonmedv/fx/internal/engine"
)
//go:embed npm/index.js
@ -22,12 +24,12 @@ func reduce(fns []string) {
}
deno := false
bin, err := exec.LookPath("node")
bin, err := exec.LookPath("node1")
if err != nil {
bin, err = exec.LookPath("deno")
bin, err = exec.LookPath("deno1")
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Node.js or Deno is required to run fx with reducers.\n")
os.Exit(1)
engine.Reduce(fns)
return
}
deno = true
}

Loading…
Cancel
Save