-
Notifications
You must be signed in to change notification settings - Fork 30
/
run.go
134 lines (111 loc) · 3.63 KB
/
run.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/spf13/cobra"
)
var runCmd = &cobra.Command{
Use: "run",
Example: runExamples(),
Short: "Execute a jk program",
Args: runArgs,
Run: run,
}
// InlineSpecifier is used as the initial module specifier when exec'ing literal JavaScript
const InlineSpecifier = "<exec>"
// StdinSpecifier is used as the initial module specifier when reading JavaScript from stdin
const StdinSpecifier = "<stdin>"
// ToplevelReferrer is used as the module referrer when using --module
const ToplevelReferrer = "<toplevel>"
func runExamples() string {
b := bytes.Buffer{}
b.WriteString(" specifying where are input files used by script and output generated files\n")
b.WriteString(" jk run -v -i ./inputdir -o ./outputdir ./scriptdir/script.js\n")
b.WriteString(" specifying input parameters\n")
b.WriteString(" jk run -v -p path.k1.k2=value ./scriptdir/script.js\n")
b.WriteString(" specifying input parameters and file containing parameters\n")
b.WriteString(" jk run -v -p key=value -f filename.json script.js\n")
b.WriteString(" run the JavaScript given on the command line, with standard lib available\n")
b.WriteString(" jk run -c log('foo')\n")
b.WriteString(" run the module given, resolved relative to the current directory\n")
b.WriteString(" jk run -m @example/module\n")
b.WriteString(" read the script to run from stdin\n")
b.WriteString(" jk run -\n")
return b.String()
}
const inlineTemplate = `
import { print, log, write, read, Format, Encoding } from '@jkcfg/std';
import { dir, info } from '@jkcfg/std/fs';
import * as param from '@jkcfg/std/param';
%s;
`
type scriptOptions struct {
// control how the argument is interpreted; by default, it's a
// file to load
module, inline bool
}
func initScriptFlags(cmd *cobra.Command, opts *scriptOptions) {
cmd.PersistentFlags().BoolVarP(&opts.module, "module", "m", false, "treat first argument as specifying a module to load")
cmd.PersistentFlags().BoolVarP(&opts.inline, "exec", "c", false, "treat first argument as specifying literal JavaScript to execute")
}
var runOptions struct {
vmOptions
scriptOptions
}
func init() {
initScriptFlags(runCmd, &runOptions.scriptOptions)
initAllVMFlags(runCmd, &runOptions.vmOptions)
jk.AddCommand(runCmd)
}
func runArgs(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("run requires an input script")
}
return nil
}
func establishScriptDir(opts scriptOptions, scriptArg string) string {
var scriptDir string
// Before setting anything else up, we have to establish the
// directory relative to which modules will be resolved.
var err error
switch {
case opts.module && runOptions.inline:
log.Fatal("supply one or neither of --module,-m and --exec,-c")
case opts.module || opts.inline || scriptArg == "-":
scriptDir, err = filepath.Abs(".")
default:
filename := scriptArg
scriptDir, err = filepath.Abs(filepath.Dir(filename))
}
if err != nil {
log.Fatal(err)
}
return scriptDir
}
func run(cmd *cobra.Command, args []string) {
scriptDir := establishScriptDir(runOptions.scriptOptions, args[0])
vm := newVM(&runOptions.vmOptions, scriptDir)
var runErr error
switch {
case runOptions.module:
runErr = vm.RunModule(args[0], ToplevelReferrer)
case runOptions.inline:
runErr = vm.Run(InlineSpecifier, fmt.Sprintf(inlineTemplate, args[0]))
case args[0] == "-":
input, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
runErr = vm.Run(StdinSpecifier, string(input))
default: // a file
runErr = vm.RunFile(args[0])
}
if runErr != nil {
log.Fatal(runErr)
}
}