├── .gitignore ├── .goreleaser.yml ├── LICENSE ├── Makefile ├── Readme.md ├── _examples └── 01-hello-world │ └── main.go ├── all_test.go ├── api ├── build │ └── build.go ├── run │ ├── run.go │ ├── run_test.go │ └── testdata │ │ └── 00-packages │ │ ├── a │ │ ├── expected.txt │ │ └── input.go │ │ ├── b │ │ ├── expected.txt │ │ └── input.go │ │ └── dep │ │ └── dep.go ├── serve │ └── serve.go └── test │ └── test.go ├── cmd └── joy │ └── main.go ├── docs.go ├── install.sh ├── internal ├── _dom │ ├── curl │ │ └── curl.go │ ├── def │ │ └── def.go │ ├── defs │ │ ├── callback.go │ │ ├── callbackinterface.go │ │ ├── dictionary.go │ │ ├── enum.go │ │ ├── interface.go │ │ ├── method.go │ │ ├── property.go │ │ └── typedef.go │ ├── graph │ │ ├── graph.go │ │ └── graph_test.go │ ├── index │ │ └── index.go │ ├── inputs │ │ ├── browser.webidl.xml │ │ ├── override.json │ │ └── sample.xml │ ├── main.go │ ├── mdn │ │ └── mdn.go │ ├── override │ │ └── override.go │ ├── parser │ │ └── parser.go │ └── raw │ │ └── raw.go ├── _vdom │ ├── _main.go │ └── inputs │ │ ├── tags.json │ │ ├── text.go │ │ └── vdom.go ├── bindata │ └── bindata.go ├── chrome │ ├── chrome.go │ ├── chrome_test.go │ ├── downloader.go │ └── formatter.go ├── cli │ ├── build │ │ └── build.go │ ├── cli.go │ ├── run │ │ └── run.go │ ├── serve │ │ └── serve.go │ ├── test │ │ └── test.go │ ├── upgrade │ │ └── upgrade.go │ └── version │ │ └── version.go ├── compiler │ ├── compiler.go │ ├── def │ │ └── def.go │ ├── defs │ │ ├── field.go │ │ ├── file.go │ │ ├── function.go │ │ ├── interface.go │ │ ├── method.go │ │ ├── process.go │ │ ├── rewrite.go │ │ ├── struct.go │ │ ├── type.go │ │ ├── value.go │ │ └── vdom.go │ ├── graph │ │ ├── graph.go │ │ └── graph_test.go │ ├── index │ │ ├── index.go │ │ └── vdom.go │ ├── indexer │ │ ├── indexer.go │ │ └── indexer_test.go │ ├── loader │ │ ├── loader.go │ │ ├── loader_test.go │ │ └── testdata │ │ │ ├── 00-basic │ │ │ └── main.go │ │ │ ├── _dom │ │ │ ├── input.go │ │ │ └── window │ │ │ │ ├── element.go │ │ │ │ ├── event.go │ │ │ │ ├── eventtarget.go │ │ │ │ ├── htmlelement.go │ │ │ │ └── node.go │ │ │ ├── internal │ │ │ └── runtime │ │ │ │ └── runtime.go │ │ │ ├── macro │ │ │ └── macro.go │ │ │ └── stdlib │ │ │ └── fmt │ │ │ └── fmt.go │ ├── scope │ │ ├── scope.go │ │ └── scope_test.go │ ├── script │ │ └── script.go │ ├── translator │ │ ├── rewrite.go │ │ ├── translator.go │ │ └── vdom.go │ ├── util │ │ └── util.go │ ├── variable │ │ ├── variable.go │ │ └── variable_test.go │ └── watch.go ├── env │ └── env.go ├── gen │ ├── builtin.go │ ├── format.go │ ├── funcs.go │ └── gen.go ├── jsast │ ├── api.go │ ├── assemble.go │ ├── parse.go │ └── syntax.go ├── livereload │ ├── connection.go │ ├── javascript.go │ └── livereload.go ├── mains │ ├── mains.go │ ├── mains_test.go │ └── testdata │ │ ├── one │ │ └── main.go │ │ └── two │ │ ├── a │ │ └── main.go │ │ └── b │ │ └── main.go ├── paths │ └── paths.go ├── prompt │ └── prompt.go ├── regenerator │ ├── co.js │ ├── es5.js │ ├── es6.js │ ├── package-lock.json │ └── package.json ├── runtime │ └── runtime.go ├── std │ └── std.go ├── testutil │ ├── copy.go │ └── format.go ├── typewriter │ └── typewriter.go └── version │ └── version.go ├── macro ├── raw.go ├── rawfile.go ├── rewrite.go └── runtime.go ├── stdlib ├── encoding │ └── json │ │ └── json.go ├── errors │ └── errors.go ├── fmt │ └── fmt.go ├── net │ └── url │ │ └── url.go ├── stdlib.go ├── strconv │ └── strconv.go ├── strings │ └── strings.go └── time │ └── time.go └── testdata ├── 01-hello-world ├── expected.js.txt ├── expected.txt └── input.go ├── 02-variables ├── expected.js.txt ├── expected.txt └── input.go ├── 03-function ├── expected.js.txt ├── expected.txt └── input.go ├── 04-if-elseif-else ├── expected.js.txt ├── expected.txt └── input.go ├── 05-for-loop ├── expected.js.txt ├── expected.txt └── input.go ├── 06-structs ├── expected.js.txt ├── expected.txt └── input.go ├── 07-struct-nested ├── expected.js.txt ├── expected.txt └── input.go ├── 08-struct-nested-array ├── expected.js.txt ├── expected.txt └── input.go ├── 09-zero-values ├── expected.js.txt ├── expected.txt └── input.go ├── 10-arrays ├── expected.js.txt ├── expected.txt └── input.go ├── 11-array-of-arrays ├── expected.js.txt ├── expected.txt └── input.go ├── 12-maps ├── expected.js.txt ├── expected.txt └── input.go ├── 13-simple-raw ├── expected.js.txt ├── expected.txt └── input.go ├── 14-empty-vars ├── expected.js.txt ├── expected.txt └── input.go ├── 15-append ├── expected.js.txt ├── expected.txt └── input.go ├── 16-range ├── expected.js.txt ├── expected.txt └── input.go ├── 20-anonymous-funcs ├── expected.js.txt ├── expected.txt └── input.go ├── 21-packages ├── deepdep │ └── deepdep.go ├── dep │ ├── another.go │ └── dep.go ├── one │ ├── expected.js.txt │ ├── expected.txt │ └── input.go └── two │ ├── expected.js.txt │ ├── expected.txt │ └── input.go ├── 22-basic-bindings ├── expected.js.txt ├── expected.txt └── input.go ├── 23-goroutine-basic ├── expected.js.txt ├── expected.txt └── input.go ├── 24-interface-array ├── expected.js.txt └── input.go ├── 25-external-file ├── expected.js.txt ├── expected.txt ├── fetch.js └── input.go ├── 26-errors ├── expected.js.txt └── input.go ├── 27-global-fetch ├── expected.js.txt ├── expected.txt ├── input.go └── promise │ └── promise.go ├── 28-if-simple-stmt ├── expected.js.txt ├── expected.txt └── input.go ├── 30-json-marshal ├── expected.js.txt ├── expected.txt └── input.go ├── 31-json-unmarshal ├── expected.js.txt ├── expected.txt └── input.go ├── 32-goroutine-deep ├── expected.js.txt ├── expected.txt └── input.go ├── 33-strings-join ├── expected.js.txt ├── expected.txt └── input.go ├── 34-keyless-fields ├── dep │ └── dep │ │ └── dep.go ├── expected.js.txt ├── expected.txt └── input.go ├── 35-interfaces ├── expected.js.txt ├── expected.txt └── input.go ├── 36-unused-interfaces ├── expected.js.txt ├── expected.txt └── input.go ├── 37-deep-interfaces ├── expected.js.txt ├── expected.txt └── input.go ├── 38-receiver-funcs ├── expected.js.txt ├── expected.txt └── input.go ├── 39-inner-interfaces ├── expected.js.txt ├── expected.txt └── input.go ├── 40-vnodes ├── expected.js.txt ├── expected.txt ├── index │ └── index.go ├── input.go └── vnode │ ├── component │ └── component.go │ ├── element │ └── element.go │ └── vnode.go ├── 42-basic-rewrite ├── expected.js.txt ├── expected.txt └── input.go ├── 43-var-function ├── expected.js.txt ├── expected.txt └── input.go ├── 44-var-files ├── expected.js.txt ├── expected.txt ├── fn.js └── input.go ├── 45-external-fetch ├── expected.js.txt ├── expected.txt ├── fetch │ ├── fetch.go │ └── unfetch.js └── input.go ├── 46-nil-defaults ├── element │ └── element.go ├── expected.js.txt ├── expected.txt └── input.go ├── 47-circular ├── element │ └── element.go ├── expected.js.txt ├── expected.txt └── input.go ├── 48-func-spread ├── expected.js.txt ├── expected.txt └── input.go ├── 50-slice-spreads ├── expected.js.txt ├── expected.txt └── input.go ├── 51-variadic-rewrite ├── expected.js.txt ├── expected.txt └── input.go ├── 53-rename-variables ├── expected.js.txt ├── expected.txt └── input.go ├── 55-struct-embedding ├── expected.js ├── expected.js.txt ├── expected.txt └── input.go ├── 57-rewrite-file-dep ├── expected.js.txt ├── expected.txt ├── fetch │ ├── fetch.go │ └── unfetch.js └── input.go ├── 58-time-sleep ├── expected.js.txt ├── expected.txt └── input.go ├── 59-rename-method ├── dep │ └── dep.go ├── expected.js.txt ├── expected.txt └── input.go ├── 61-method-func-name ├── expected.js.txt ├── expected.txt └── input.go ├── 62-rename-interface-methods ├── expected.js.txt ├── expected.txt └── input.go ├── 64-other-type-defs ├── dep │ └── dep.go ├── expected.js.txt ├── expected.txt └── input.go ├── 65-dup-method-struct ├── expected.js.txt ├── expected.txt └── input.go ├── 66-iface-rewrites ├── expected.js.txt ├── expected.txt └── input.go ├── 67-return-values ├── expected.js.txt ├── expected.txt └── input.go ├── 68-methodless-structs ├── expected.js.txt ├── expected.txt └── input.go ├── 70-stdlib ├── expected.js.txt ├── expected.txt └── input.go ├── _41-named-results ├── dep │ └── dep.go └── main.go ├── _47-circular ├── element │ └── element.go ├── expected.js.txt ├── expected.txt └── input.go ├── _49-jsx ├── expected.js.txt ├── expected.txt ├── header │ └── header.go ├── index.html ├── input.go ├── preact │ ├── preact.go │ └── preact.js └── window │ └── window.go ├── _52-basic-dom ├── expected.js.txt ├── expected.txt └── input.go ├── _54-basic-jolly ├── expected.js.txt ├── expected.txt ├── header │ └── header.go ├── index.html ├── input.go └── preact │ ├── preact.go │ └── preact.js ├── _60-chaining ├── expected.js.txt ├── expected.txt ├── header │ └── header.go ├── input.go └── preact │ ├── preact.go │ └── preact.js ├── _63-vdom-zero-values ├── expected.js.txt ├── expected.txt ├── input.go └── preact.js ├── _69-dom-iface-rewrites ├── expected.js.txt ├── expected.txt └── input.go ├── _71-vdom ├── expected.js.txt ├── expected.txt ├── input.go └── preact.js ├── _72-defer ├── expected.js.txt └── input.go ├── _73-init ├── expected.js.txt └── input.go ├── _74-switch ├── expected.js.txt └── input.go └── _75-select ├── expected.js.txt └── input.go /.gitignore: -------------------------------------------------------------------------------- 1 | /chrome 2 | /.envrc 3 | /dist 4 | /dom 5 | /vdom -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | build: 2 | main: cmd/joy/main.go 3 | binary: joy 4 | ldflags: -s -w 5 | -X github.com/matthewmueller/joy/internal/version.Version={{.Version}} 6 | -X main.commit={{.Commit}} 7 | -X main.date={{.Date}} 8 | -X github.com/matthewmueller/joy/internal/env.awsAccessKey={{.Env.JOY_AWS_ACCESS_ID}} 9 | -X github.com/matthewmueller/joy/internal/env.awsSecretKey={{.Env.JOY_AWS_SECRET_ACCESS_KEY}} 10 | -X github.com/matthewmueller/joy/internal/env.awsRegion={{.Env.JOY_AWS_REGION}} 11 | -X github.com/matthewmueller/joy/internal/env.firehoseStream={{.Env.JOY_AWS_FIREHOSE_STREAM}} 12 | goos: 13 | - darwin 14 | - linux 15 | - windows 16 | - freebsd 17 | - netbsd 18 | - openbsd 19 | goarch: 20 | - amd64 21 | - 386 22 | changelog: 23 | sort: asc 24 | filters: 25 | exclude: 26 | - '^docs:' 27 | - '^refactor' 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) 2 | 3 | INFOLOG := \033[34m ▸\033[0m 4 | WARNLOG := \033[33m ▸\033[0m 5 | ERROLOG := \033[31m ⨯\033[0m 6 | 7 | TEST:="./..." 8 | 9 | test: embed 10 | @echo "$(INFOLOG) Running go tests..." 11 | @go test -timeout=10m $(TEST) 12 | .PHONY: tests 13 | 14 | # Install the commands. 15 | install: 16 | @echo "$(INFOLOG) Installing Joy to the path..." 17 | @go install ./cmd/... 18 | .PHONY: install 19 | 20 | embed: 21 | @go-bindata -o ./internal/bindata/bindata.go -nometadata -pkg bindata ./internal/runtime ./macro/* ./stdlib/**/* 22 | @gofmt -w ./internal/bindata/ 23 | .PHONY: embed 24 | 25 | dom: 26 | @echo "$(INFOLOG) Generating the DOM..." 27 | @go run internal/dom/main.go 28 | .PHONY: dom 29 | 30 | vdom: 31 | @echo "$(INFOLOG) Generating the virtual DOM..." 32 | @go run internal/vdom/main.go 33 | .PHONY: vdom 34 | 35 | # Release binaries to GitHub. 36 | release: embed 37 | @read -p "> What's the new version? (e.g. 0.1.0) " version; \ 38 | echo "$(INFOLOG) Releasing... v$$version"; \ 39 | git tag v$$version; \ 40 | goreleaser -p 1 --rm-dist --config .goreleaser.yml; 41 | .PHONY: release 42 | 43 | # Show source statistics. 44 | cloc: 45 | @cloc -exclude-dir=vendor,node_modules,dom,vdom,chrome . 46 | .PHONY: cloc 47 | 48 | # Show to-do items per file. 49 | todo: 50 | @grep \ 51 | --exclude-dir=vendor \ 52 | --exclude-dir=node_modules \ 53 | --exclude-dir=chrome \ 54 | --exclude-dir=vdom \ 55 | --exclude-dir=dom \ 56 | --exclude=Makefile \ 57 | --text \ 58 | --color \ 59 | -nRo -E ' TODO:.*|SkipNow' . 60 | .PHONY: todo -------------------------------------------------------------------------------- /_examples/01-hello-world/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("hello world!") 7 | } 8 | -------------------------------------------------------------------------------- /api/build/build.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/apex/log" 7 | "github.com/matthewmueller/joy/internal/mains" 8 | "github.com/matthewmueller/joy/internal/paths" 9 | 10 | "github.com/matthewmueller/joy/internal/compiler" 11 | "github.com/matthewmueller/joy/internal/compiler/script" 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | // Config struct 16 | type Config struct { 17 | Context context.Context 18 | Packages []string 19 | Development bool 20 | JoyPath string 21 | Log log.Interface // Log (optional) 22 | } 23 | 24 | func (c *Config) defaults() error { 25 | if c.Context == nil { 26 | c.Context = context.Background() 27 | } 28 | 29 | if c.Log == nil { 30 | c.Log = log.Log 31 | } 32 | 33 | if c.JoyPath == "" { 34 | p, err := paths.Joy() 35 | if err != nil { 36 | return errors.Wrapf(err, "error getting joy's root path") 37 | } 38 | c.JoyPath = p 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // Build fn 45 | func Build(cfg *Config) (scripts []*script.Script, err error) { 46 | if err := cfg.defaults(); err != nil { 47 | return scripts, errors.Wrapf(err, "error setting defaults") 48 | } 49 | 50 | packages, err := mains.Find(cfg.Packages...) 51 | if err != nil { 52 | return scripts, errors.Wrapf(err, "error finding mains") 53 | } 54 | 55 | scripts, err = compiler.Compile(&compiler.Config{ 56 | Packages: packages, 57 | Development: cfg.Development, 58 | JoyPath: cfg.JoyPath, 59 | }) 60 | if err != nil { 61 | return scripts, errors.Wrapf(err, "error compiling") 62 | } 63 | 64 | return scripts, nil 65 | } 66 | -------------------------------------------------------------------------------- /api/run/run.go: -------------------------------------------------------------------------------- 1 | // Package run compiles your Go file and runs it on headless chrome 2 | // 3 | // This is the programmatic API to running Joy. Run will be called 4 | // by the CLI: 5 | // 6 | // $ joy run main.go 7 | // 8 | package run 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | "path" 14 | 15 | "github.com/apex/log" 16 | "github.com/matthewmueller/joy/internal/mains" 17 | 18 | "github.com/matthewmueller/joy/internal/chrome" 19 | "github.com/matthewmueller/joy/internal/compiler" 20 | "github.com/matthewmueller/joy/internal/paths" 21 | "github.com/pkg/errors" 22 | ) 23 | 24 | // Config struct 25 | type Config struct { 26 | FilePath string // Go file to compile (required) 27 | Context context.Context // Cancellable context (optional, default: background) 28 | Development bool // Run the development bundle (optional, default: true) 29 | JoyPath string // Root path to Joy's state files (optional) 30 | Log log.Interface // Logger to use (optional) 31 | } 32 | 33 | func (c *Config) defaults() error { 34 | if c.Context == nil { 35 | c.Context = context.Background() 36 | } 37 | 38 | if c.Log == nil { 39 | c.Log = log.Log 40 | } 41 | 42 | if c.JoyPath == "" { 43 | p, err := paths.Joy() 44 | if err != nil { 45 | return errors.Wrapf(err, "error getting joy's root path") 46 | } 47 | c.JoyPath = p 48 | } 49 | 50 | return nil 51 | } 52 | 53 | // Run fn 54 | func Run(cfg *Config) (result string, err error) { 55 | if err := cfg.defaults(); err != nil { 56 | return result, errors.Wrapf(err, "error getting defaults") 57 | } 58 | 59 | packages, err := mains.Find(cfg.FilePath) 60 | if err != nil { 61 | return result, errors.Wrapf(err, "error getting mains for filepath") 62 | } 63 | 64 | files, err := compiler.Compile(&compiler.Config{ 65 | Packages: packages, 66 | JoyPath: cfg.JoyPath, 67 | Development: cfg.Development, 68 | }) 69 | if err != nil { 70 | return result, errors.Wrapf(err, "unable to compile file") 71 | } else if len(files) == 0 { 72 | return result, fmt.Errorf("a main file requires a main function to build") 73 | } else if len(files) != 1 { 74 | return result, fmt.Errorf("joy run expects only 1 main file, but received %d files", len(files)) 75 | } 76 | 77 | // download chrome if it doesn't already exist 78 | ch, err := chrome.Start(cfg.Context, path.Join(cfg.JoyPath, "chrome")) 79 | if err != nil { 80 | return result, errors.Wrapf(err, "error starting chrome") 81 | } 82 | defer ch.Close() 83 | 84 | tar, err := ch.Target() 85 | if err != nil { 86 | return result, errors.Wrapf(err, "error leasing target") 87 | } 88 | defer tar.Close() 89 | 90 | result, err = tar.Run(files[0].Source()) 91 | if err != nil { 92 | return result, errors.Wrapf(err, "error running chrome target") 93 | } 94 | 95 | return result, nil 96 | } 97 | -------------------------------------------------------------------------------- /api/run/testdata/00-packages/a/expected.txt: -------------------------------------------------------------------------------- 1 | dep: a -------------------------------------------------------------------------------- /api/run/testdata/00-packages/a/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/matthewmueller/joy/api/run/testdata/00-packages/dep" 7 | ) 8 | 9 | func main() { 10 | fmt.Println(dep.Dep("a")) 11 | } 12 | -------------------------------------------------------------------------------- /api/run/testdata/00-packages/b/expected.txt: -------------------------------------------------------------------------------- 1 | dep: b -------------------------------------------------------------------------------- /api/run/testdata/00-packages/b/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/matthewmueller/joy/api/run/testdata/00-packages/dep" 7 | ) 8 | 9 | func main() { 10 | fmt.Println(dep.Dep("b")) 11 | } 12 | -------------------------------------------------------------------------------- /api/run/testdata/00-packages/dep/dep.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | // Dep fn 4 | func Dep(from string) string { 5 | return "dep: " + from 6 | } 7 | -------------------------------------------------------------------------------- /api/test/test.go: -------------------------------------------------------------------------------- 1 | // Package test is coming soon 2 | package test 3 | -------------------------------------------------------------------------------- /cmd/joy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/matthewmueller/joy/internal/cli" 10 | "github.com/matthewmueller/joy/internal/version" 11 | 12 | "github.com/apex/log" 13 | logcli "github.com/apex/log/handlers/cli" 14 | ) 15 | 16 | func main() { 17 | ctx := trap(syscall.SIGINT, syscall.SIGTERM) 18 | log.SetHandler(logcli.Default) 19 | 20 | err := cli.Run(ctx, version.Version) 21 | if err != nil { 22 | log.Fatal(err.Error()) 23 | os.Exit(1) 24 | } 25 | } 26 | 27 | func trap(sig ...os.Signal) context.Context { 28 | ctx, cancel := context.WithCancel(context.Background()) 29 | go func() { 30 | c := make(chan os.Signal) 31 | signal.Notify(c, sig...) 32 | defer signal.Stop(c) 33 | 34 | select { 35 | case <-ctx.Done(): 36 | case <-c: 37 | cancel() 38 | } 39 | }() 40 | 41 | return ctx 42 | } 43 | -------------------------------------------------------------------------------- /docs.go: -------------------------------------------------------------------------------- 1 | // Package joy is a compiler that translates idiomatic Go into concise 2 | // Javascript that works in every browser. Use Go's type system and 3 | // world-class tooling to build large web applications with confidence. 4 | package joy 5 | -------------------------------------------------------------------------------- /internal/_dom/curl/curl.go: -------------------------------------------------------------------------------- 1 | package curl 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | // JSON a raw url 10 | func JSON(url string) (string, error) { 11 | req, err := http.NewRequest("GET", url, nil) 12 | if err != nil { 13 | return "", err 14 | } 15 | 16 | req.Header.Add("Accept", "application/json") 17 | req.Header.Add("Content-Type", "application/json") 18 | 19 | res, err := http.DefaultClient.Do(req) 20 | if err != nil { 21 | return "", err 22 | } 23 | defer res.Body.Close() 24 | 25 | buf, err := ioutil.ReadAll(res.Body) 26 | if err != nil { 27 | return "", err 28 | } 29 | 30 | // https://stackoverflow.com/a/31399046/145435 31 | buf = bytes.TrimPrefix(buf, []byte("\xef\xbb\xbf")) 32 | 33 | return string(buf), nil 34 | } 35 | 36 | // XML a raw url 37 | func XML(url string) (string, error) { 38 | req, err := http.NewRequest("GET", url, nil) 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | res, err := http.DefaultClient.Do(req) 44 | if err != nil { 45 | return "", err 46 | } 47 | defer res.Body.Close() 48 | 49 | buf, err := ioutil.ReadAll(res.Body) 50 | if err != nil { 51 | return "", err 52 | } 53 | 54 | // https://stackoverflow.com/a/31399046/145435 55 | buf = bytes.TrimPrefix(buf, []byte("\xef\xbb\xbf")) 56 | 57 | return string(buf), nil 58 | } 59 | -------------------------------------------------------------------------------- /internal/_dom/def/def.go: -------------------------------------------------------------------------------- 1 | package def 2 | 3 | // Definition interface 4 | type Definition interface { 5 | ID() string 6 | Name() string 7 | Kind() string 8 | Type(caller string) (string, error) 9 | Dependencies() ([]Definition, error) 10 | Generate() (string, error) 11 | 12 | // This comes after the definion is 13 | // already made is sort of hacky 14 | // TODO: come up with a better solution 15 | SetPackage(pkg string) 16 | GetPackage() string 17 | 18 | SetFile(file string) 19 | GetFile() string 20 | } 21 | -------------------------------------------------------------------------------- /internal/_dom/defs/callback.go: -------------------------------------------------------------------------------- 1 | package defs 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/internal/dom/def" 5 | "github.com/matthewmueller/joy/internal/dom/index" 6 | "github.com/matthewmueller/joy/internal/dom/raw" 7 | "github.com/matthewmueller/joy/internal/gen" 8 | ) 9 | 10 | var _ Callback = (*cb)(nil) 11 | 12 | // NewCallback create a callback 13 | func NewCallback(index index.Index, data *raw.Callback) Callback { 14 | return &cb{ 15 | data: data, 16 | index: index, 17 | } 18 | } 19 | 20 | // Callback interface 21 | type Callback interface { 22 | def.Definition 23 | } 24 | 25 | // cb struct 26 | type cb struct { 27 | data *raw.Callback 28 | pkg string 29 | file string 30 | 31 | index index.Index 32 | } 33 | 34 | // ID cb 35 | func (d *cb) ID() string { 36 | return d.data.Name 37 | } 38 | 39 | // Name cb 40 | func (d *cb) Name() string { 41 | return d.data.Name 42 | } 43 | 44 | // Kind cb 45 | func (d *cb) Kind() string { 46 | return "CALLBACK" 47 | } 48 | 49 | func (d *cb) Type(caller string) (string, error) { 50 | data := struct { 51 | Params []gen.Vartype 52 | Result gen.Vartype 53 | }{} 54 | 55 | for _, param := range d.data.Params { 56 | t, err := d.index.Coerce(d.pkg, param.Type) 57 | if err != nil { 58 | return "", err 59 | } 60 | data.Params = append(data.Params, gen.Vartype{ 61 | Var: gen.Identifier(param.Name), 62 | Optional: param.Optional, 63 | Type: t, 64 | }) 65 | } 66 | 67 | t, err := d.index.Coerce(d.pkg, d.data.Type) 68 | if err != nil { 69 | return "", err 70 | } 71 | data.Result = gen.Vartype{ 72 | Var: gen.Identifier(d.data.Name), 73 | Type: t, 74 | } 75 | 76 | if t == "" { 77 | return gen.Generate("callback_fn/"+d.data.Name, data, `func ({{ joinvt .Params }})`) 78 | } 79 | 80 | return gen.Generate("callback_fn/"+d.data.Name, data, `func ({{ joinvt .Params }}) ({{ vt .Result }})`) 81 | } 82 | 83 | func (d *cb) SetPackage(pkg string) { 84 | d.pkg = pkg 85 | } 86 | func (d *cb) GetPackage() string { 87 | return d.pkg 88 | } 89 | 90 | func (d *cb) SetFile(file string) { 91 | d.file = file 92 | } 93 | func (d *cb) GetFile() string { 94 | return d.file 95 | } 96 | 97 | func (d *cb) Dependencies() (defs []def.Definition, err error) { 98 | for _, param := range d.data.Params { 99 | if def := d.index.Find(param.Type); def != nil { 100 | defs = append(defs, def) 101 | } 102 | } 103 | return defs, nil 104 | } 105 | 106 | func (d *cb) Generate() (string, error) { 107 | return "", nil 108 | } 109 | -------------------------------------------------------------------------------- /internal/_dom/defs/enum.go: -------------------------------------------------------------------------------- 1 | package defs 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/internal/dom/def" 5 | "github.com/matthewmueller/joy/internal/dom/index" 6 | "github.com/matthewmueller/joy/internal/dom/raw" 7 | "github.com/matthewmueller/joy/internal/gen" 8 | ) 9 | 10 | var _ Enum = (*enum)(nil) 11 | 12 | // NewEnum create a callback 13 | func NewEnum(index index.Index, data *raw.Enum) Enum { 14 | return &enum{ 15 | data: data, 16 | index: index, 17 | } 18 | } 19 | 20 | // Enum interface 21 | type Enum interface { 22 | def.Definition 23 | } 24 | 25 | // Enum struct 26 | type enum struct { 27 | data *raw.Enum 28 | pkg string 29 | file string 30 | 31 | index index.Index 32 | } 33 | 34 | // ID fn 35 | func (d *enum) ID() string { 36 | return d.data.Name 37 | } 38 | 39 | // Name fn 40 | func (d *enum) Name() string { 41 | return d.data.Name 42 | } 43 | 44 | // Kind fn 45 | func (d *enum) Kind() string { 46 | return "ENUM" 47 | } 48 | 49 | func (d *enum) Type(caller string) (string, error) { 50 | if caller == d.pkg { 51 | return gen.Pointer(gen.Capitalize(d.data.Name)), nil 52 | } 53 | return gen.Pointer(d.pkg + "." + gen.Capitalize(d.data.Name)), nil 54 | } 55 | 56 | func (d *enum) SetPackage(pkg string) { 57 | d.pkg = pkg 58 | } 59 | func (d *enum) GetPackage() string { 60 | return d.pkg 61 | } 62 | 63 | func (d *enum) SetFile(file string) { 64 | d.file = file 65 | } 66 | func (d *enum) GetFile() string { 67 | return d.file 68 | } 69 | 70 | // Children fn 71 | func (d *enum) Dependencies() (defs []def.Definition, err error) { 72 | return defs, nil 73 | } 74 | 75 | // Generate fn 76 | func (d *enum) Generate() (string, error) { 77 | data := struct { 78 | Package string 79 | Name string 80 | }{ 81 | Package: d.pkg, 82 | Name: gen.Capitalize(d.data.Name), 83 | } 84 | 85 | return gen.Generate("enum/"+d.data.Name, data, ` 86 | type {{ .Name }} string 87 | `) 88 | } 89 | -------------------------------------------------------------------------------- /internal/_dom/defs/typedef.go: -------------------------------------------------------------------------------- 1 | package defs 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/internal/dom/def" 5 | "github.com/matthewmueller/joy/internal/dom/index" 6 | "github.com/matthewmueller/joy/internal/dom/raw" 7 | "github.com/matthewmueller/joy/internal/gen" 8 | ) 9 | 10 | var _ TypeDef = (*typedef)(nil) 11 | 12 | // NewTypeDef fn 13 | func NewTypeDef(index index.Index, data *raw.TypeDef) TypeDef { 14 | return &typedef{ 15 | index: index, 16 | data: data, 17 | } 18 | } 19 | 20 | // TypeDef interface 21 | type TypeDef interface { 22 | def.Definition 23 | } 24 | 25 | type typedef struct { 26 | data *raw.TypeDef 27 | pkg string 28 | file string 29 | 30 | index index.Index 31 | } 32 | 33 | // ID fn 34 | func (d *typedef) ID() string { 35 | return d.data.NewType 36 | } 37 | 38 | // Name fn 39 | func (d *typedef) Name() string { 40 | return d.data.NewType 41 | } 42 | 43 | // Kind fn 44 | func (d *typedef) Kind() string { 45 | return "TYPEDEF" 46 | } 47 | 48 | func (d *typedef) Type(caller string) (string, error) { 49 | if caller == d.pkg { 50 | return gen.Pointer(gen.Capitalize(d.data.NewType)), nil 51 | } 52 | return gen.Pointer(d.pkg + "." + gen.Capitalize(d.data.NewType)), nil 53 | } 54 | 55 | func (d *typedef) SetPackage(pkg string) { 56 | d.pkg = pkg 57 | } 58 | func (d *typedef) GetPackage() string { 59 | return d.pkg 60 | } 61 | 62 | func (d *typedef) SetFile(file string) { 63 | d.file = file 64 | } 65 | func (d *typedef) GetFile() string { 66 | return d.file 67 | } 68 | 69 | // // Parents fn 70 | // func (d *Dictionary) Parents() []def.Definition { 71 | // return nil 72 | // } 73 | 74 | // // Ancestors fn 75 | // func (d *Dictionary) Ancestors() []def.Definition { 76 | // return nil 77 | // } 78 | 79 | // Dependencies fn 80 | func (d *typedef) Dependencies() (defs []def.Definition, err error) { 81 | if def := d.index.Find(d.data.Type); def != nil { 82 | defs = append(defs, def) 83 | } 84 | return defs, nil 85 | } 86 | 87 | func (d *typedef) Generate() (string, error) { 88 | return "", nil 89 | } 90 | -------------------------------------------------------------------------------- /internal/_dom/graph/graph_test.go: -------------------------------------------------------------------------------- 1 | package graph_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matthewmueller/joy/internal/dom/graph" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var _ graph.Node = (*testNode)(nil) 11 | 12 | type testNode struct { 13 | id string 14 | } 15 | 16 | func (t *testNode) ID() string { 17 | return t.id 18 | } 19 | 20 | func node(id string) *testNode { 21 | return &testNode{id} 22 | } 23 | 24 | func TestCliqueOne(t *testing.T) { 25 | a := node("A") 26 | b := node("B") 27 | c := node("C") 28 | d := node("D") 29 | e := node("E") 30 | f := node("F") 31 | g := node("G") 32 | h := node("H") 33 | i := node("I") 34 | j := node("J") 35 | k := node("K") 36 | 37 | gr := graph.New() 38 | gr.Edge(a, b) 39 | gr.Edge(b, c) 40 | gr.Edge(c, a) 41 | gr.Edge(b, d) 42 | gr.Edge(d, e) 43 | gr.Edge(e, f) 44 | gr.Edge(f, d) 45 | gr.Edge(g, f) 46 | gr.Edge(g, h) 47 | gr.Edge(h, i) 48 | gr.Edge(i, j) 49 | gr.Edge(j, g) 50 | gr.Edge(j, k) 51 | 52 | // for i, clique := range gr.Cliques() { 53 | // log.Infof("%dth clique", i) 54 | // for _, node := range clique { 55 | // log.Infof("node %s", node.ID()) 56 | // } 57 | // } 58 | assert.Equal(t, 4, len(gr.Cliques())) 59 | // assert.Equal(t, 3, len(gr.Cliques()[0])) 60 | } 61 | 62 | // func TestCliqueTwo(t *testing.T) { 63 | // a := node("A") 64 | // b := node("B") 65 | // c := node("C") 66 | // d := node("D") 67 | 68 | // g := graph.New() 69 | // g.Edge(a, b) 70 | // g.Edge(b, c) 71 | // g.Edge(c, a) 72 | // g.Edge(c, d) 73 | 74 | // assert.Equal(t, 2, len(g.Cliques())) 75 | // assert.Equal(t, 3, len(g.Cliques()[0])) 76 | // assert.Equal(t, 1, len(g.Cliques()[1])) 77 | // } 78 | -------------------------------------------------------------------------------- /internal/_dom/inputs/override.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "replace", 4 | "data": { 5 | "SVGElement": { 6 | "properties": [ 7 | { 8 | "name": "dataset", 9 | "type": "DOMStringMap", 10 | "readonly": true, 11 | "comment": "A DOMString representing the value of the id attribute on the given element, or the empty string if id is not present." 12 | }, 13 | { 14 | "name": "xmlBase", 15 | "type": "DOMString", 16 | "readonly": true, 17 | "comment": "A DOMString corresponding to the xml:base attribute on the given element." 18 | }, 19 | { 20 | "name": "ownerSVGElement", 21 | "type": "SVGSVGElement", 22 | "readonly": true, 23 | "comment": "An SVGSVGElement referring to the nearest ancestor element. null if the given element is the outermost element." 24 | }, 25 | { 26 | "name": "viewportElement", 27 | "type": "SVGElement", 28 | "readonly": true, 29 | "comment": "The SVGElement, which established the current viewport. Often, the nearest ancestor element. null if the given element is the outermost element." 30 | } 31 | ] 32 | } 33 | } 34 | } 35 | ] -------------------------------------------------------------------------------- /internal/_dom/mdn/mdn.go: -------------------------------------------------------------------------------- 1 | package mdn 2 | -------------------------------------------------------------------------------- /internal/_dom/override/override.go: -------------------------------------------------------------------------------- 1 | // Package override TODO 2 | package override 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "path" 9 | "runtime" 10 | 11 | "github.com/apex/log" 12 | "github.com/matthewmueller/joy/internal/dom/index" 13 | ) 14 | 15 | type task struct { 16 | Type string `json:"type,omitempty"` 17 | Data map[string]*data `json:"data,omitempty"` 18 | } 19 | 20 | type data struct { 21 | Properties *[]struct { 22 | Name string `json:"name,omitempty"` 23 | Type string `json:"type,omitempty"` 24 | Readonly bool `json:"readonly,omitempty"` 25 | Comment string `json:"comment,omitempty"` 26 | } `json:"properties,omitempty"` 27 | } 28 | 29 | // Override fn 30 | func Override(idx index.Index) (index.Index, error) { 31 | _, file, _, ok := runtime.Caller(0) 32 | if !ok { 33 | return idx, fmt.Errorf("error getting file") 34 | } 35 | 36 | dir := path.Dir(file) 37 | override, err := ioutil.ReadFile(path.Join(dir, "..", "inputs", "override.json")) 38 | if err != nil { 39 | return idx, err 40 | } 41 | 42 | var tasks []task 43 | if err := json.Unmarshal(override, &tasks); err != nil { 44 | return idx, err 45 | } 46 | 47 | for _, task := range tasks { 48 | switch task.Type { 49 | case "replace": 50 | if err := replace(idx, task.Data); err != nil { 51 | return idx, err 52 | } 53 | } 54 | } 55 | 56 | return idx, nil 57 | } 58 | 59 | func replace(idx index.Index, changes map[string]*data) error { 60 | for id, r := range changes { 61 | def := idx.Find(id) 62 | if def == nil { 63 | continue 64 | } 65 | 66 | log.Infof("got def=%s", def.ID()) 67 | _ = r 68 | // def, isset := idx[id] 69 | // if !isset { 70 | // continue 71 | // } 72 | 73 | // def. 74 | } 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /internal/_vdom/inputs/text.go: -------------------------------------------------------------------------------- 1 | package vdom 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // Text struct 6 | // js:"text,omit" 7 | type Text struct { 8 | Component 9 | Node 10 | 11 | value string 12 | } 13 | 14 | // S creates a string 15 | func S(value string) *Text { 16 | macro.Rewrite("$1", value) 17 | return &Text{ 18 | value: value, 19 | } 20 | } 21 | 22 | // Render returns itself 23 | func (s *Text) Render() Node { 24 | return s 25 | } 26 | 27 | // String returns the text node value 28 | func (s *Text) String() string { 29 | return s.value 30 | } 31 | -------------------------------------------------------------------------------- /internal/_vdom/inputs/vdom.go: -------------------------------------------------------------------------------- 1 | package vdom 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/window" 5 | "github.com/matthewmueller/joy/macro" 6 | ) 7 | 8 | //go:generate go run internal/gen.go 9 | 10 | // Use fn 11 | // js:"Use,omit" 12 | func Use(pragma, filepath string) { 13 | } 14 | 15 | // Pragma is a reference to how elements get created 16 | // js:"Pragma,omit" 17 | func Pragma() string { 18 | return "" 19 | } 20 | 21 | // File is a reference to the vdom library itself 22 | // js:"File,omit" 23 | func File() string { 24 | return "" 25 | } 26 | 27 | // Component struct 28 | type Component interface { 29 | // js:"render" 30 | Render() Node 31 | // js:"setState" 32 | SetState(state interface{}) 33 | // js:"forceUpdate" 34 | ForceUpdate() 35 | } 36 | 37 | // Child interface 38 | type Child interface { 39 | Render() Node 40 | } 41 | 42 | // Node interface 43 | type Node interface { 44 | String() string 45 | } 46 | 47 | // Render the component 48 | func Render(component Child, parent window.Node, merge window.Node) { 49 | macro.Rewrite("$1.render($2, $3, $4)", File(), component, parent, merge) 50 | } 51 | 52 | // String turns the component into a string 53 | func String(component Child) string { 54 | return component.Render().String() 55 | } 56 | -------------------------------------------------------------------------------- /internal/chrome/chrome_test.go: -------------------------------------------------------------------------------- 1 | package chrome_test 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" 6 | "path" 7 | "testing" 8 | 9 | "github.com/matthewmueller/joy/internal/paths" 10 | 11 | "github.com/apex/log" 12 | "github.com/matthewmueller/joy/internal/chrome" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestRun(t *testing.T) { 17 | ctx := context.Background() 18 | 19 | root, err := paths.Joy() 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | exists: 25 | chromePath, err := chrome.Exists(path.Join(root, "chrome")) 26 | if err != nil { 27 | t.Fatal(err) 28 | } else if chromePath == "" { 29 | log.Infof("downloading headless chrome (this only needs to be done once)") 30 | if err := chrome.Download(path.Join(root, "chrome")); err != nil { 31 | t.Fatal(err) 32 | } 33 | goto exists 34 | } 35 | 36 | ch, err := chrome.New(ctx, &chrome.Settings{ 37 | ExecutablePath: chromePath, 38 | Stderr: ioutil.Discard, 39 | Stdout: ioutil.Discard, 40 | }) 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | defer ch.Close() 45 | 46 | target, err := ch.Target() 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | result, err := target.Run(` 52 | console.log("hiya", 5) 53 | `) 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | 58 | assert.Equal(t, "hiya 5", result) 59 | 60 | if e := target.Close(); e != nil { 61 | t.Fatal(e) 62 | } 63 | if e := ch.Close(); e != nil { 64 | t.Fatal(e) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /internal/chrome/formatter.go: -------------------------------------------------------------------------------- 1 | package chrome 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/mafredri/cdp/protocol/runtime" 9 | ) 10 | 11 | func formatValue(obj runtime.RemoteObject) string { 12 | preview := obj.Preview 13 | value := obj.Value 14 | 15 | switch obj.Type { 16 | case "object": 17 | if obj.Subtype == nil { 18 | return preview.String() 19 | } 20 | switch *obj.Subtype { 21 | case "array": 22 | if len(preview.Properties) == 0 { 23 | return "[]" 24 | } 25 | 26 | var arr []string 27 | for _, prop := range preview.Properties { 28 | arr = append(arr, formatProperty(prop)) 29 | } 30 | return "[ " + strings.Join(arr, ", ") + " ]" 31 | case "error": 32 | var arr []string 33 | for _, prop := range preview.Properties { 34 | arr = append(arr, formatProperty(prop)) 35 | } 36 | return strings.Join(arr, "\n") 37 | default: 38 | bytes, _ := json.Marshal(preview) 39 | return string(bytes) 40 | } 41 | case "string": 42 | v, e := strconv.Unquote(string(value)) 43 | // ignore error if there is one 44 | if e != nil { 45 | return string(value) 46 | } 47 | return v 48 | default: 49 | return string(value) 50 | } 51 | } 52 | 53 | func formatProperty(prop runtime.PropertyPreview) string { 54 | if prop.Value == nil { 55 | return prop.String() 56 | } 57 | 58 | value := *prop.Value 59 | switch prop.Type { 60 | case "string": 61 | return `'` + value + `'` 62 | default: 63 | return prop.String() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /internal/cli/build/build.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | 9 | "github.com/apex/log" 10 | "github.com/matthewmueller/joy/api/build" 11 | "github.com/pkg/errors" 12 | kingpin "gopkg.in/alecthomas/kingpin.v2" 13 | ) 14 | 15 | // New build command 16 | func New(ctx context.Context, root *kingpin.Application) { 17 | cmd := root.Command("build", "build Go packages into Javascript") 18 | packages := cmd.Arg("packages", "packages to build").Required().Strings() 19 | dev := cmd.Flag("dev", "generate a development build").Short('d').Bool() 20 | output := cmd.Flag("output", "directory to output files").Short('o').String() 21 | joyPath := cmd.Flag("joy", "Joy state path").Hidden().String() 22 | 23 | cmd.Action(func(_ *kingpin.ParseContext) (err error) { 24 | if !*dev { 25 | log.Infof("Production builds coming soon! for now use `joy build --dev ...` and run regenerator and uglify manually") 26 | return nil 27 | } else if *output != "" { 28 | log.Infof("Output templates coming soon! For now you can use `joy build --dev ...`") 29 | return nil 30 | } 31 | 32 | scripts, err := build.Build(&build.Config{ 33 | Context: ctx, 34 | Packages: *packages, 35 | Development: *dev, 36 | JoyPath: *joyPath, 37 | }) 38 | if err != nil { 39 | return errors.Wrap(err, "error building packages") 40 | } 41 | 42 | cwd, err := os.Getwd() 43 | if err != nil { 44 | return errors.Wrapf(err, "error getting cwd") 45 | } 46 | 47 | var loc int 48 | for _, file := range scripts { 49 | filename := path.Base(file.Name()) 50 | if err := ioutil.WriteFile(path.Join(cwd, filename+".js"), []byte(file.Source()), 0644); err != nil { 51 | return errors.Wrapf(err, "error writing %s", file.Path()) 52 | } 53 | 54 | // count lines of code to be able 55 | // to map filesize to performance 56 | loc += len(file.Source()) 57 | } 58 | 59 | return nil 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /internal/cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/matthewmueller/joy/internal/prompt" 9 | "github.com/matthewmueller/store" 10 | 11 | "github.com/pkg/errors" 12 | 13 | apibuild "github.com/matthewmueller/joy/api/build" 14 | "github.com/matthewmueller/joy/internal/cli/build" 15 | "github.com/matthewmueller/joy/internal/cli/run" 16 | "github.com/matthewmueller/joy/internal/cli/serve" 17 | "github.com/matthewmueller/joy/internal/cli/test" 18 | "github.com/matthewmueller/joy/internal/cli/upgrade" 19 | "github.com/matthewmueller/joy/internal/cli/version" 20 | kingpin "gopkg.in/alecthomas/kingpin.v2" 21 | ) 22 | 23 | // Run the CLI 24 | func Run(ctx context.Context, ver string) (err error) { 25 | cmd := kingpin.New("joy", "Joy – A Delightful Go to Javascript Compiler") 26 | cmd.Version(ver) 27 | 28 | // setup our local db 29 | db, err := store.New("joy") 30 | if err != nil { 31 | return errors.Wrapf(err, "unable to setup the storage") 32 | } 33 | 34 | if ver != "master" { 35 | if done, err := prompt.Prompt(db); err != nil || done { 36 | return errors.Wrapf(err, "prompt error") 37 | } 38 | } 39 | 40 | // special case: joy ./main & ./main.go 41 | if len(os.Args[1:]) == 1 { 42 | if _, err := os.Stat(os.Args[1]); !os.IsNotExist(err) { 43 | files, err := apibuild.Build(&apibuild.Config{ 44 | Context: ctx, 45 | Packages: []string{os.Args[1]}, 46 | }) 47 | if err != nil { 48 | return errors.Wrapf(err, "error building code") 49 | } else if len(files) == 0 { 50 | return fmt.Errorf("a main file requires a main function to build") 51 | } else if len(files) != 1 { 52 | return fmt.Errorf("joy run expects only 1 main file, but received %d files", len(files)) 53 | } 54 | 55 | fmt.Println(files[0].Source()) 56 | return nil 57 | } 58 | } 59 | 60 | // commands 61 | run.New(ctx, cmd) 62 | build.New(ctx, cmd) 63 | serve.New(ctx, cmd) 64 | test.New(ctx, cmd) 65 | upgrade.New(ctx, cmd, ver) 66 | version.New(ctx, cmd, ver) 67 | 68 | // run the command 69 | _, err = cmd.Parse(os.Args[1:]) 70 | return err 71 | } 72 | -------------------------------------------------------------------------------- /internal/cli/run/run.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/apex/log" 8 | "github.com/matthewmueller/joy/api/run" 9 | kingpin "gopkg.in/alecthomas/kingpin.v2" 10 | ) 11 | 12 | // New build command 13 | func New(ctx context.Context, root *kingpin.Application) { 14 | cmd := root.Command("run", "compile and run a Go file") 15 | filePath := cmd.Arg("file", "Go file to compile and run").Required().String() 16 | // dev := cmd.Flag("dev", "generate a development build").Short('d').Bool() 17 | joyPath := cmd.Flag("joy", "Joy state path").Hidden().String() 18 | 19 | cmd.Action(func(_ *kingpin.ParseContext) (err error) { 20 | result, err := run.Run(&run.Config{ 21 | Context: ctx, 22 | FilePath: *filePath, 23 | Development: true, // TODO: change 24 | // Development: *dev 25 | JoyPath: *joyPath, 26 | }) 27 | if err != nil { 28 | log.WithError(err).Error("error running script") 29 | return err 30 | } 31 | 32 | fmt.Println(result) 33 | return nil 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /internal/cli/serve/serve.go: -------------------------------------------------------------------------------- 1 | package serve 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/matthewmueller/joy/api/serve" 8 | "github.com/pkg/errors" 9 | kingpin "gopkg.in/alecthomas/kingpin.v2" 10 | ) 11 | 12 | // New build command 13 | func New(ctx context.Context, root *kingpin.Application) { 14 | cmd := root.Command("serve", "start a development server with livereload") 15 | packages := cmd.Arg("packages", "packages to bundle").Required().Strings() 16 | port := cmd.Flag("port", "port to serve from").Short('p').Default("8080").String() 17 | // dev := cmd.Flag("dev", "generate a development build").Short('d').Bool() 18 | joyPath := cmd.Flag("joy", "Joy state path").Hidden().String() 19 | 20 | cmd.Action(func(_ *kingpin.ParseContext) (err error) { 21 | port, e := strconv.Atoi(*port) 22 | if e != nil { 23 | return errors.Wrap(e, "invalid port") 24 | } 25 | 26 | // serve the files 27 | if err := serve.Serve(&serve.Config{ 28 | Context: ctx, 29 | Packages: *packages, 30 | Port: port, 31 | Development: true, 32 | JoyPath: *joyPath, 33 | }); err != nil { 34 | return errors.Wrapf(err, "error serving") 35 | } 36 | 37 | return nil 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /internal/cli/test/test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/apex/log" 7 | kingpin "gopkg.in/alecthomas/kingpin.v2" 8 | ) 9 | 10 | // New test command 11 | func New(ctx context.Context, root *kingpin.Application) { 12 | cmd := root.Command("test", "run go tests against headless chrome") 13 | verbose := cmd.Flag("verbose", "verbose flag").Short('v').Bool() 14 | pkgs := cmd.Arg("packages", "packages to run the tests on").Required().Strings() 15 | _, _ = verbose, pkgs 16 | cmd.Action(func(_ *kingpin.ParseContext) (err error) { 17 | // packages, err := mains.Find(*pkgs...) 18 | // if err != nil { 19 | // return err 20 | // } 21 | log.Infof("Headless testing is coming soon!") 22 | return nil 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /internal/cli/upgrade/upgrade.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "runtime" 13 | "strings" 14 | 15 | "github.com/apex/log" 16 | "github.com/google/go-github/github" 17 | kingpin "gopkg.in/alecthomas/kingpin.v2" 18 | ) 19 | 20 | // New build command 21 | func New(ctx context.Context, root *kingpin.Application, version string) { 22 | cmd := root.Command("upgrade", "upgrade joy to the latest version") 23 | cmd.Action(func(_ *kingpin.ParseContext) (err error) { 24 | return upgrade(ctx, version) 25 | }) 26 | } 27 | 28 | // https://github.com/apex/apex/blob/master/upgrade/upgrade.go 29 | // Thanks TJ 30 | func upgrade(ctx context.Context, version string) (err error) { 31 | log.Infof("current release is %s", version) 32 | 33 | // fetch releases 34 | gh := github.NewClient(nil) 35 | releases, _, err := gh.Repositories.ListReleases(ctx, "matthewmueller", "joy", nil) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | // see if it's new 41 | latest := releases[0] 42 | log.Infof("latest release is %s", *latest.TagName) 43 | latestVersion := (*latest.TagName)[1:] 44 | 45 | // TODO: ignore if semver 46 | if latestVersion == version { 47 | log.Infof("you're up to date :)") 48 | return nil 49 | } 50 | 51 | asset := findAsset(latest) 52 | if asset == nil { 53 | return errors.New("cannot find binary for your system") 54 | } 55 | 56 | // get the executable's path 57 | cmdPath, err := exec.LookPath("joy") 58 | if err != nil { 59 | return err 60 | } 61 | cmdDir := filepath.Dir(cmdPath) 62 | 63 | // create tmp file 64 | tmpPath := filepath.Join(cmdDir, "joy-upgrade") 65 | f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | // download binary 71 | log.Infof("downloading %s", *asset.BrowserDownloadURL) 72 | res, err := http.Get(*asset.BrowserDownloadURL) 73 | if err != nil { 74 | return err 75 | } 76 | defer res.Body.Close() 77 | 78 | // copy it down 79 | _, err = io.Copy(f, res.Body) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | // replace it 85 | log.Infof("replacing %s", cmdPath) 86 | err = os.Rename(tmpPath, cmdPath) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | log.Infof("visit https://github.com/matthewmueller/joy/releases for the changelog") 92 | return nil 93 | } 94 | 95 | // findAsset returns the binary for this platform. 96 | func findAsset(release *github.RepositoryRelease) *github.ReleaseAsset { 97 | for _, asset := range release.Assets { 98 | version := strings.TrimPrefix(*release.TagName, "v") 99 | if *asset.Name == fmt.Sprintf("joy_%s_%s_%s.tar.gz", version, runtime.GOOS, runtime.GOARCH) { 100 | return &asset 101 | } 102 | } 103 | return nil 104 | } 105 | -------------------------------------------------------------------------------- /internal/cli/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/apex/log" 7 | kingpin "gopkg.in/alecthomas/kingpin.v2" 8 | ) 9 | 10 | // New build command 11 | func New(ctx context.Context, root *kingpin.Application, version string) { 12 | cmd := root.Command("version", "get the current version") 13 | cmd.Action(func(_ *kingpin.ParseContext) error { 14 | log.Infof(version) 15 | return nil 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /internal/compiler/def/def.go: -------------------------------------------------------------------------------- 1 | package def 2 | 3 | import ( 4 | "go/ast" 5 | "go/types" 6 | ) 7 | 8 | // ID interface 9 | type ID interface { 10 | ID() string 11 | } 12 | 13 | // Definition interface 14 | type Definition interface { 15 | ID 16 | 17 | Path() string 18 | Name() string 19 | OriginalName() string 20 | Exported() bool 21 | Omitted() bool 22 | Type() types.Type 23 | Kind() string 24 | Dependencies() ([]Definition, error) 25 | Imports() map[string]string 26 | FromRuntime() bool 27 | } 28 | 29 | // Field interface 30 | type Field interface { 31 | Name() string 32 | Type() ast.Expr 33 | } 34 | 35 | // Rewritee is the original method or function getting rewritten 36 | type Rewritee interface { 37 | ID() string 38 | Params() []string 39 | Rewrite() Rewrite 40 | IsVariadic() bool 41 | } 42 | 43 | // Rewrite interface 44 | type Rewrite interface { 45 | Rewritee() Rewritee 46 | Expression() string 47 | Vars() []RewriteVariable 48 | } 49 | 50 | // RewriteVariable interface 51 | type RewriteVariable interface { 52 | Definition() Definition 53 | Node() ast.Expr 54 | String() (string, error) 55 | } 56 | 57 | // FunctionResult interface 58 | type FunctionResult interface { 59 | Name() string 60 | Definition() Definition 61 | } 62 | 63 | // InterfaceMethod interface 64 | type InterfaceMethod interface { 65 | Rewritee 66 | 67 | OriginalName() string 68 | Name() string 69 | } 70 | -------------------------------------------------------------------------------- /internal/compiler/defs/field.go: -------------------------------------------------------------------------------- 1 | package defs 2 | 3 | import ( 4 | "go/ast" 5 | 6 | "github.com/matthewmueller/joy/internal/compiler/util" 7 | ) 8 | 9 | // field of a struct 10 | type field struct { 11 | name string 12 | tag util.JSTag 13 | kind ast.Expr 14 | embedded bool 15 | } 16 | 17 | func (f *field) Name() string { 18 | if f.tag.Rename != "" { 19 | return f.tag.Rename 20 | } 21 | return f.name 22 | } 23 | 24 | func (f *field) Type() ast.Expr { 25 | return f.kind 26 | } 27 | 28 | func (f *field) Embedded() bool { 29 | return f.embedded 30 | } 31 | -------------------------------------------------------------------------------- /internal/compiler/defs/file.go: -------------------------------------------------------------------------------- 1 | package defs 2 | 3 | import ( 4 | "go/types" 5 | "path" 6 | 7 | "github.com/matthewmueller/joy/internal/compiler/def" 8 | "github.com/matthewmueller/joy/internal/compiler/util" 9 | ) 10 | 11 | // Filer interface 12 | type Filer interface { 13 | def.Definition 14 | Implicit() bool 15 | } 16 | 17 | // files struct 18 | type files struct { 19 | id string 20 | path string 21 | name string 22 | implicit bool 23 | } 24 | 25 | var _ Filer = (*files)(nil) 26 | 27 | var filecache = map[string]def.Definition{} 28 | 29 | // File constructor 30 | // 31 | // NOTE: implicit includes are files that have been added 32 | // during the process, that have no representation 33 | // in the source code (e.g. preact := js.File("./preact.js")) 34 | // you shouldn't need this option unless you're working on 35 | // the compiler 36 | func File(packagePath, relative string, implicit bool) (def.Definition, error) { 37 | src, e := util.GoSourcePath() 38 | if e != nil { 39 | return nil, e 40 | } 41 | 42 | pkgpath := path.Join(packagePath, relative) 43 | fullpath := path.Join(src, packagePath, relative) 44 | 45 | if filecache[fullpath] != nil { 46 | return filecache[fullpath], nil 47 | } 48 | 49 | def := &files{ 50 | id: fullpath, 51 | path: pkgpath, 52 | implicit: implicit, 53 | name: path.Base(fullpath), 54 | } 55 | 56 | filecache[fullpath] = def 57 | return def, nil 58 | } 59 | 60 | func (d *files) ID() string { 61 | return d.id 62 | } 63 | 64 | func (d *files) Name() string { 65 | return d.name 66 | } 67 | 68 | func (d *files) OriginalName() string { 69 | return d.name 70 | } 71 | 72 | func (d *files) Path() string { 73 | return d.path 74 | } 75 | 76 | func (d *files) Dependencies() (deps []def.Definition, err error) { 77 | return deps, nil 78 | } 79 | 80 | func (d *files) Exported() bool { 81 | return false 82 | } 83 | 84 | func (d *files) Type() types.Type { 85 | return nil 86 | } 87 | 88 | func (d *files) Omitted() bool { 89 | return false 90 | } 91 | 92 | func (d *files) Kind() string { 93 | return "FILE" 94 | } 95 | 96 | func (d *files) FromRuntime() bool { 97 | return false 98 | } 99 | 100 | func (d *files) Imports() map[string]string { 101 | return map[string]string{} 102 | } 103 | 104 | func (d *files) Implicit() bool { 105 | return d.implicit 106 | } 107 | -------------------------------------------------------------------------------- /internal/compiler/defs/rewrite.go: -------------------------------------------------------------------------------- 1 | package defs 2 | 3 | import ( 4 | "go/ast" 5 | 6 | "github.com/matthewmueller/joy/internal/compiler/def" 7 | "github.com/matthewmueller/joy/internal/compiler/util" 8 | ) 9 | 10 | var _ def.Rewrite = (*rewrite)(nil) 11 | var _ def.RewriteVariable = (*variable)(nil) 12 | 13 | type rewrite struct { 14 | rewritee def.Rewritee 15 | expr string 16 | vars []def.RewriteVariable // variables passed into macro.Rewrite(expr, ) 17 | } 18 | 19 | func (r *rewrite) Rewritee() def.Rewritee { 20 | return r.rewritee 21 | } 22 | 23 | func (r *rewrite) Expression() string { 24 | return r.expr 25 | } 26 | 27 | func (r *rewrite) Vars() []def.RewriteVariable { 28 | return r.vars 29 | } 30 | 31 | type variable struct { 32 | def def.Definition 33 | node ast.Expr 34 | } 35 | 36 | func (v *variable) Definition() def.Definition { 37 | return v.def 38 | } 39 | 40 | func (v *variable) Node() ast.Expr { 41 | return v.node 42 | } 43 | 44 | func (v *variable) String() (string, error) { 45 | return util.ExprToString(v.node) 46 | } 47 | -------------------------------------------------------------------------------- /internal/compiler/graph/graph.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // Graph struct 8 | type Graph struct { 9 | nodes map[string]Node 10 | edges map[string]map[string]bool 11 | } 12 | 13 | // Node interface 14 | type Node interface { 15 | ID() string 16 | Path() string 17 | } 18 | 19 | // New function 20 | func New() *Graph { 21 | return &Graph{ 22 | nodes: map[string]Node{}, 23 | edges: map[string]map[string]bool{}, 24 | } 25 | } 26 | 27 | // Edge adds an edge 28 | func (g *Graph) Edge(parent Node, child Node) { 29 | g.nodes[parent.ID()] = parent 30 | 31 | if parent.ID() == child.ID() { 32 | return 33 | } 34 | g.nodes[child.ID()] = child 35 | 36 | if g.edges[parent.ID()] == nil { 37 | g.edges[parent.ID()] = map[string]bool{} 38 | } 39 | g.edges[parent.ID()][child.ID()] = true 40 | } 41 | 42 | // Toposort sorts topologically 43 | func (g *Graph) Toposort(node Node) (sorted []string) { 44 | sorted = g.dfs(node, nil) 45 | sorted = g.reverseUniqueByPath(sorted) 46 | // log.Infof("path %s => %+v", path, sorted) 47 | return sorted 48 | } 49 | 50 | func (g *Graph) dfs(node Node, visited map[string]bool) (order []string) { 51 | if visited == nil { 52 | visited = map[string]bool{} 53 | } 54 | 55 | visited[node.ID()] = true 56 | 57 | children := g.edges[node.ID()] 58 | sortedChildren := sortKeys(children) 59 | for _, child := range sortedChildren { 60 | if visited[child] { 61 | continue 62 | } 63 | o := g.dfs(g.nodes[child], visited) 64 | order = append(order, o...) 65 | } 66 | 67 | order = append(order, node.ID()) 68 | return order 69 | } 70 | 71 | func (g *Graph) reverseUniqueByPath(ids []string) []string { 72 | l := len(ids) 73 | 74 | buckets := map[string][]string{} 75 | order := make([]string, 0, l) 76 | seen := make(map[string]bool) 77 | 78 | // get the unique paths in reverse order 79 | // to maintain the topology 80 | for i := l - 1; i >= 0; i-- { 81 | id := ids[i] 82 | node := g.nodes[id] 83 | path := node.Path() 84 | 85 | if buckets[path] == nil { 86 | buckets[path] = []string{} 87 | } 88 | buckets[path] = append(buckets[path], id) 89 | 90 | if _, ok := seen[path]; !ok { 91 | seen[path] = true 92 | order = append(order, path) 93 | } 94 | } 95 | 96 | ids = []string{} 97 | // order of paths now needs to be reversed 98 | // from the previous operation 99 | for i := len(order) - 1; i >= 0; i-- { 100 | path := order[i] 101 | bucket := buckets[path] 102 | // the bucket order also needs to be reversed 103 | // to maintain the ids within the path's topology 104 | for j := len(bucket) - 1; j >= 0; j-- { 105 | ids = append(ids, bucket[j]) 106 | } 107 | } 108 | 109 | return ids 110 | } 111 | 112 | func sortKeys(m map[string]bool) (sorted []string) { 113 | for k := range m { 114 | sorted = append(sorted, k) 115 | } 116 | sort.Strings(sorted) 117 | return sorted 118 | } 119 | -------------------------------------------------------------------------------- /internal/compiler/graph/graph_test.go: -------------------------------------------------------------------------------- 1 | package graph_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/apex/log" 7 | "github.com/matthewmueller/joy/internal/compiler/graph" 8 | ) 9 | 10 | var _ graph.Node = (*testNode)(nil) 11 | 12 | type testNode struct { 13 | id string 14 | path string 15 | } 16 | 17 | func (t *testNode) ID() string { 18 | return t.id 19 | } 20 | 21 | func (t *testNode) Path() string { 22 | return t.path 23 | } 24 | 25 | func node(id, path string) graph.Node { 26 | return &testNode{ 27 | id: id, 28 | path: path, 29 | } 30 | } 31 | 32 | func TestTopo(t *testing.T) { 33 | g := graph.New() 34 | am := node("am", "main") 35 | bm := node("bm", "main") 36 | cm := node("cm", "main") 37 | a1 := node("a1", "1") 38 | b1 := node("b1", "1") 39 | c1 := node("c1", "1") 40 | a2 := node("a2", "2") 41 | b2 := node("b2", "2") 42 | c2 := node("c2", "2") 43 | 44 | a3 := node("a3", "3") 45 | a4 := node("a4", "4") 46 | 47 | g.Edge(am, bm) 48 | g.Edge(am, cm) 49 | g.Edge(cm, a1) 50 | 51 | g.Edge(a1, a1) 52 | g.Edge(a1, b1) 53 | g.Edge(b1, c1) 54 | 55 | // disconnected 56 | g.Edge(c1, a2) 57 | g.Edge(a2, b2) 58 | g.Edge(b2, c2) 59 | 60 | g.Edge(c2, a4) 61 | g.Edge(a3, a4) 62 | 63 | g.Edge(am, a3) 64 | 65 | sorted := g.Toposort(am) 66 | log.Infof("sorted %+v", sorted) 67 | } 68 | -------------------------------------------------------------------------------- /internal/compiler/index/vdom.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/matthewmueller/joy/internal/compiler/def" 7 | ) 8 | 9 | // SetVDOMPragma sets up JSX 10 | func (i *Index) SetVDOMPragma(pragma string) { 11 | i.vdom.Pragma = pragma 12 | } 13 | 14 | // SetVDOMFile sets the vdom file 15 | func (i *Index) SetVDOMFile(def def.Definition) { 16 | i.vdom.File = def 17 | } 18 | 19 | // VDOMFile gets the vdom file 20 | func (i *Index) VDOMFile() (def.Definition, error) { 21 | if i.vdom.File == nil { 22 | return nil, errors.New("JSX not setup. Please use jsx.Use(pragma, filepath) in an init()") 23 | } 24 | return i.vdom.File, nil 25 | } 26 | 27 | // VDOMPragma gets our jsx settings if we've specified them 28 | func (i *Index) VDOMPragma() (string, error) { 29 | if i.vdom.Pragma == "" { 30 | return "", errors.New("JSX not setup. Please use jsx.Use(pragma, filepath) in an init()") 31 | } 32 | return i.vdom.Pragma, nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/00-basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/matthewmueller/joy/macro" 7 | ) 8 | 9 | func main() { 10 | macro.Raw("console.log") 11 | fmt.Println("hi world") 12 | } 13 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/_dom/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/internal/compiler/loader/testdata/dom/window" 4 | 5 | func Element() window.Element { 6 | return nil 7 | } 8 | 9 | func main() { 10 | html := Element().(window.HTMLElement) 11 | html.DispatchEvent() 12 | } 13 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/_dom/window/element.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | // Element interface 4 | type Element interface { 5 | Node 6 | } 7 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/_dom/window/event.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | // Event interface 4 | type Event interface { 5 | SrcElement() Element 6 | } 7 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/_dom/window/eventtarget.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | // EventTarget 4 | type EventTarget interface { 5 | DispatchEvent() Event 6 | } 7 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/_dom/window/htmlelement.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | type HTMLElement interface { 4 | Element 5 | } 6 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/_dom/window/node.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | type Node interface { 4 | DispatchEvent() Event 5 | 6 | ParentElement() HTMLElement 7 | } 8 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/internal/runtime/runtime.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | // Runtime function 4 | func Runtime() { 5 | println("runtime") 6 | } 7 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/macro/macro.go: -------------------------------------------------------------------------------- 1 | package macro 2 | 3 | // Raw fn 4 | func Raw(js string) { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /internal/compiler/loader/testdata/stdlib/fmt/fmt.go: -------------------------------------------------------------------------------- 1 | package fmt 2 | 3 | // Println fn 4 | func Println(a ...interface{}) (n int, err error) { 5 | println(a) 6 | return 0, nil 7 | } 8 | -------------------------------------------------------------------------------- /internal/compiler/scope/scope.go: -------------------------------------------------------------------------------- 1 | package scope 2 | 3 | import "go/ast" 4 | 5 | // Scope type 6 | type Scope struct { 7 | Owner ast.Node 8 | Outer *Scope 9 | scope *ast.Scope 10 | } 11 | 12 | // File scope is the outermost scope (I think... :-P) 13 | // func File(scope *ast.Scope) *Scope { 14 | // return &Scope{ 15 | // scope: scope, 16 | // } 17 | // } 18 | 19 | // New Scope based on an old scope 20 | func New(owner ast.Node) *Scope { 21 | return &Scope{ 22 | Owner: owner, 23 | scope: ast.NewScope(nil), 24 | } 25 | } 26 | 27 | // Within looks up a name within a scope (doesn't traverse upward) 28 | func (s *Scope) Within(name string) *ast.Object { 29 | return s.scope.Lookup(name) 30 | } 31 | 32 | // Lookup name in scope and all parent scopes 33 | func (s *Scope) Lookup(name string) *ast.Object { 34 | o := s.scope.Lookup(name) 35 | if o != nil { 36 | return o 37 | } 38 | if s.Outer == nil { 39 | return nil 40 | } 41 | return s.Outer.Lookup(name) 42 | } 43 | 44 | // Insert attempts to insert a named object obj into the scope s. 45 | // If the scope already contains an object alt with the same name, 46 | // Insert leaves the scope unchanged and returns alt. Otherwise 47 | // it inserts obj and returns nil. 48 | func (s *Scope) Insert(obj *ast.Object) (alt *ast.Object) { 49 | return s.scope.Insert(obj) 50 | } 51 | 52 | // Debugging support 53 | func (s *Scope) String() string { 54 | return s.scope.String() 55 | } 56 | -------------------------------------------------------------------------------- /internal/compiler/scope/scope_test.go: -------------------------------------------------------------------------------- 1 | package scope_test 2 | 3 | import ( 4 | "go/ast" 5 | "go/parser" 6 | "go/token" 7 | "testing" 8 | ) 9 | 10 | func parse(t *testing.T, src string) *ast.File { 11 | fset := token.NewFileSet() 12 | f, e := parser.ParseFile(fset, "", src, 0) 13 | if e != nil { 14 | t.Fatal(e) 15 | } 16 | return f 17 | } 18 | 19 | // func TestFunctionScope(t *testing.T) { 20 | // f := parse(t, ` 21 | // package main 22 | // func main() { 23 | // a := 3 24 | // } 25 | // `) 26 | 27 | // filescope := scope.File(f.Scope) 28 | // main := f.Decls[0].(*ast.FuncDecl) 29 | // mainscope := filescope.New(main) 30 | 31 | // a := main.Body.List[0].(*ast.AssignStmt).Lhs[0].(*ast.Ident) 32 | // mainscope.Insert(a.Obj) 33 | 34 | // assert.NotNil(t, filescope.Lookup("main")) 35 | // assert.Nil(t, filescope.Lookup("a")) 36 | 37 | // assert.NotNil(t, mainscope.Lookup("main")) 38 | // assert.NotNil(t, mainscope.Lookup("a")) 39 | // assert.Nil(t, mainscope.Within("main")) 40 | // assert.NotNil(t, mainscope.Within("a")) 41 | 42 | // assert.NotNil(t, mainscope.Outer.Lookup("main")) 43 | // assert.Nil(t, mainscope.Outer.Lookup("a")) 44 | // } 45 | 46 | // func TestFunctionLit(t *testing.T) { 47 | // f := parse(t, ` 48 | // package main 49 | // func main() { 50 | // b := 0 51 | // func() { 52 | // a := 3 53 | // }() 54 | // } 55 | // `) 56 | 57 | // filescope := scope.File(f.Scope) 58 | 59 | // main := f.Decls[0].(*ast.FuncDecl) 60 | // mainscope := filescope.New(main) 61 | 62 | // b := main.Body.List[0].(*ast.AssignStmt).Lhs[0].(*ast.Ident) 63 | // mainscope.Insert(b.Obj) 64 | 65 | // anon := main.Body.List[1].(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.FuncLit) 66 | // anonscope := mainscope.New(anon) 67 | 68 | // a := anon.Body.List[0].(*ast.AssignStmt).Lhs[0].(*ast.Ident) 69 | // anonscope.Insert(a.Obj) 70 | 71 | // assert.NotNil(t, filescope.Lookup("main")) 72 | // assert.Nil(t, filescope.Lookup("a")) 73 | 74 | // assert.NotNil(t, mainscope.Lookup("main")) 75 | // assert.NotNil(t, mainscope.Lookup("b")) 76 | // assert.Nil(t, mainscope.Lookup("a")) 77 | // assert.NotNil(t, mainscope.Owner) 78 | 79 | // assert.NotNil(t, anonscope.Lookup("main")) 80 | // assert.NotNil(t, anonscope.Lookup("b")) 81 | // assert.NotNil(t, anonscope.Lookup("a")) 82 | // assert.NotNil(t, anonscope.Owner) 83 | 84 | // assert.Nil(t, anonscope.Within("main")) 85 | // assert.Nil(t, anonscope.Within("b")) 86 | // assert.NotNil(t, anonscope.Lookup("a")) 87 | 88 | // assert.NotNil(t, anonscope.Outer.Lookup("main")) 89 | // assert.Nil(t, anonscope.Outer.Lookup("a")) 90 | 91 | // assert.NotNil(t, anonscope.Outer.Outer.Lookup("main")) 92 | // assert.Nil(t, anonscope.Outer.Outer.Lookup("a")) 93 | 94 | // // assert.NotNil(t, mainscope.Outer.Lookup("main")) 95 | // // assert.Nil(t, mainscope.Outer.Lookup("a")) 96 | // } 97 | -------------------------------------------------------------------------------- /internal/compiler/script/script.go: -------------------------------------------------------------------------------- 1 | package script 2 | 3 | // Script struct 4 | type Script struct { 5 | name string 6 | path string 7 | source string 8 | } 9 | 10 | // New script 11 | func New(name, path, source string) *Script { 12 | return &Script{name, path, source} 13 | } 14 | 15 | // Name fn 16 | func (s *Script) Name() string { 17 | return s.name 18 | } 19 | 20 | // Path fn 21 | func (s *Script) Path() string { 22 | return s.path 23 | } 24 | 25 | // Source fn 26 | func (s *Script) Source() string { 27 | return s.source 28 | } 29 | -------------------------------------------------------------------------------- /internal/compiler/watch.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | // Watch a set of packages 4 | func Watch(packages ...string) { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /internal/env/env.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import "os" 4 | 5 | // e.g. 6 | // go run -ldflags="-X 'github.com/matthewmueller/joy/internal/env.awsAccessKey=hello'" _examples/env/env.go 7 | var awsAccessKey = "" 8 | var awsSecretKey = "" 9 | var awsRegion = "" 10 | var firehoseStream = "" 11 | 12 | // Env is the environment 13 | type Env struct { 14 | AWSAccessKey string 15 | AWSSecretKey string 16 | AWSRegion string 17 | FirehoseStream string 18 | } 19 | 20 | // Get the environment 21 | func Get() Env { 22 | return Env{ 23 | AWSAccessKey: getEnv("JOY_AWS_ACCESS_ID", awsAccessKey), 24 | AWSSecretKey: getEnv("JOY_AWS_SECRET_ACCESS_KEY", awsSecretKey), 25 | AWSRegion: getEnv("JOY_AWS_REGION", awsRegion), 26 | FirehoseStream: getEnv("JOY_AWS_FIREHOSE_STREAM", firehoseStream), 27 | } 28 | } 29 | 30 | func getEnv(name string, def string) string { 31 | if os.Getenv(name) != "" { 32 | return os.Getenv(name) 33 | } 34 | return def 35 | } 36 | -------------------------------------------------------------------------------- /internal/gen/builtin.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | // builtins map 4 | var builtins = map[string]string{ 5 | "bool": "boolean", 6 | "byte": "b", 7 | "complex64": "c64", 8 | "complex128": "c128", 9 | "error": "err", 10 | "float32": "f32", 11 | "float64": "f64", 12 | "int": "integer", 13 | "int8": "integer8", 14 | "int16": "integer16", 15 | "int32": "integer32", 16 | "int64": "integer64", 17 | "rune": "ru", 18 | "string": "str", 19 | "uint": "uinteger", 20 | "uint8": "uinteger8", 21 | "uint16": "uinteger16", 22 | "uint32": "uinteger32", 23 | "uint64": "uinteger64", 24 | "uintptr": "uintpointer", 25 | "true": "yes", 26 | "false": "no", 27 | "iota": "ita", 28 | "nil": "null", 29 | "append": "app", 30 | "cap": "capacity", 31 | "close": "cls", 32 | "complex": "cpx", 33 | "copy": "cpy", 34 | "delete": "del", 35 | "imag": "img", 36 | "len": "l", 37 | "make": "mk", 38 | "new": "nw", 39 | "panic": "pnc", 40 | "print": "prt", 41 | "println": "prtln", 42 | "real": "rl", 43 | "recover": "rec", 44 | "break": "brk", 45 | "default": "def", 46 | "func": "fn", 47 | "func()": "fn", 48 | "interface": "iface", 49 | "interface{}": "iface", 50 | "select": "sel", 51 | "case": "cs", 52 | "defer": "dfr", 53 | "go": "g", 54 | "map": "mp", 55 | "struct": "structure", 56 | "chan": "chn", 57 | "else": "els", 58 | "goto": "gto", 59 | "package": "pkg", 60 | "switch": "swch", 61 | "const": "cst", 62 | "fallthrough": "flth", 63 | "if": "ifs", 64 | "range": "rng", 65 | "type": "kind", 66 | "continue": "cont", 67 | "for": "fors", 68 | "import": "imp", 69 | "return": "ret", 70 | "var": "v", 71 | } 72 | 73 | // default map 74 | var defaults = map[string]interface{}{ 75 | "bool": false, 76 | "byte": 0, 77 | "complex64": 0, 78 | "complex128": 0, 79 | "error": nil, 80 | "float32": 0, 81 | "float64": 0, 82 | "int": 0, 83 | "int8": 0, 84 | "int16": 0, 85 | "int32": 0, 86 | "int64": 0, 87 | "rune": 0, 88 | "string": "", 89 | "uint": 0, 90 | "uint8": 0, 91 | "uint16": 0, 92 | "uint32": 0, 93 | "uint64": 0, 94 | "uintptr": 0, 95 | "nil": nil, 96 | "complex": 0, 97 | "real": 0, 98 | "func": nil, 99 | "interface": nil, 100 | "map": nil, 101 | "struct": nil, 102 | "chan": nil, 103 | } 104 | -------------------------------------------------------------------------------- /internal/gen/format.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | "os/exec" 8 | 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // Format the output using goimports 13 | func Format(input string) (output string, err error) { 14 | cmd := exec.Command("goimports") 15 | stdin, err := cmd.StdinPipe() 16 | 17 | if err != nil { 18 | return output, err 19 | } 20 | stdout, err := cmd.StdoutPipe() 21 | if err != nil { 22 | return output, err 23 | } 24 | stderr, err := cmd.StderrPipe() 25 | if err != nil { 26 | return output, err 27 | } 28 | 29 | reader := bytes.NewBufferString(input) 30 | 31 | if e := cmd.Start(); e != nil { 32 | return output, e 33 | } 34 | 35 | io.Copy(stdin, reader) 36 | stdin.Close() 37 | 38 | formatted, err := ioutil.ReadAll(stdout) 39 | if err != nil { 40 | return output, err 41 | } 42 | 43 | formattingError, err := ioutil.ReadAll(stderr) 44 | if err != nil { 45 | return output, err 46 | } 47 | 48 | stderr.Close() 49 | stdout.Close() 50 | 51 | if e := cmd.Wait(); e != nil { 52 | return output, errors.New(string(formattingError)) 53 | } 54 | 55 | return string(formatted), nil 56 | } 57 | 58 | // FormatAll formats by a path overwriting the original file 59 | func FormatAll(dir string) (err error) { 60 | cmd := exec.Command("goimports", "-w", dir) 61 | 62 | stderr, err := cmd.StderrPipe() 63 | if err != nil { 64 | return err 65 | } 66 | defer stderr.Close() 67 | 68 | if e := cmd.Start(); e != nil { 69 | return errors.Wrapf(err, "error running goimports") 70 | } 71 | 72 | formattingError, err := ioutil.ReadAll(stderr) 73 | if err != nil { 74 | return errors.Wrapf(err, "stderr error: %s", formattingError) 75 | } 76 | 77 | if err := cmd.Wait(); err != nil { 78 | return errors.Wrapf(err, "error running goimports\n%s", formattingError) 79 | } 80 | 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /internal/gen/funcs.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "text/template" 7 | 8 | "github.com/knq/snaker" 9 | ) 10 | 11 | func funcs() template.FuncMap { 12 | return template.FuncMap{ 13 | "capitalize": Capitalize, 14 | "lowercase": Lowercase, 15 | "pointer": Pointer, 16 | "variable": Variable, 17 | "joinvt": JoinVT, 18 | "joinv": JoinV, 19 | "join": Join, 20 | "vt": VT, 21 | "sequence": Sequence, 22 | "identifier": Identifier, 23 | } 24 | } 25 | 26 | // Identifier from the string avoiding the builtins 27 | func Identifier(s string) string { 28 | out := []string{} 29 | for i, w := range strings.Split(s, "-") { 30 | if i == 0 { 31 | out = append(out, w) 32 | continue 33 | } 34 | 35 | capitalize := strings.ToUpper(w[:1]) + strings.ToLower(w[1:]) 36 | out = append(out, capitalize) 37 | } 38 | s = strings.Join(out, "") 39 | 40 | out = []string{} 41 | for i, w := range strings.Split(s, "_") { 42 | if i == 0 { 43 | out = append(out, w) 44 | continue 45 | } 46 | 47 | capitalize := strings.ToUpper(w[:1]) + strings.ToLower(w[1:]) 48 | out = append(out, capitalize) 49 | } 50 | s = strings.Join(out, "") 51 | 52 | if builtins[s] != "" { 53 | return builtins[s] 54 | } 55 | return s 56 | } 57 | 58 | // Capitalize helper 59 | func Capitalize(s string) string { 60 | s = snaker.SnakeToCamelIdentifier(snaker.CamelToSnake(s)) 61 | return Identifier(s) 62 | } 63 | 64 | // Lowercase fn 65 | func Lowercase(s string) string { 66 | s = strings.ToLower(snaker.SnakeToCamelIdentifier(s)) 67 | return Identifier(s) 68 | } 69 | 70 | // Variable creates a short variable from the string 71 | func Variable(s string) string { 72 | s = strings.TrimLeft(s, "*[]") 73 | if len(s) == 0 { 74 | return s 75 | } 76 | letter := s[:1] 77 | return Lowercase(letter) 78 | } 79 | 80 | // Pointer returns a pointer version of the variable 81 | func Pointer(s string) string { 82 | if zero, isset := defaults[s]; isset && zero == nil { 83 | return s 84 | } 85 | return "*" + strings.TrimLeft(s, "*") 86 | } 87 | 88 | // VT turns a vartype into a string 89 | func VT(vt Vartype) string { 90 | name := Identifier(vt.Var) 91 | if vt.Optional { 92 | return name + " " + Pointer(vt.Type) 93 | } 94 | return name + " " + vt.Type 95 | } 96 | 97 | // JoinVT joins vartypes var and type 98 | func JoinVT(vts []Vartype) string { 99 | var a []string 100 | for _, vt := range vts { 101 | a = append(a, VT(vt)) 102 | } 103 | return strings.Join(a, ", ") 104 | } 105 | 106 | // JoinV joins vartypes just by the var 107 | func JoinV(vts []Vartype) string { 108 | var a []string 109 | for _, vt := range vts { 110 | a = append(a, Identifier(vt.Var)) 111 | } 112 | return strings.Join(a, ", ") 113 | } 114 | 115 | // Join just joins 2 strings by commas 116 | func Join(a []string) string { 117 | return strings.Join(a, ", ") 118 | } 119 | 120 | // Sequence counts up until a number 121 | // starting at $1 122 | func Sequence(until int) []string { 123 | var n []string 124 | for i := 0; i < until; i++ { 125 | n = append(n, "$"+strconv.Itoa(i+1)) 126 | } 127 | return n 128 | } 129 | -------------------------------------------------------------------------------- /internal/gen/gen.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | ) 7 | 8 | // File that's been generated 9 | type File struct { 10 | Name string 11 | Source string 12 | } 13 | 14 | // Vartype struct 15 | type Vartype struct { 16 | Var string 17 | Type string 18 | Optional bool 19 | } 20 | 21 | // Generate function 22 | func Generate(name string, data interface{}, tpl string) (string, error) { 23 | t, err := template.New(name).Funcs(funcs()).Parse(tpl) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | var b bytes.Buffer 29 | if e := t.Execute(&b, data); e != nil { 30 | return "", e 31 | } 32 | 33 | return string(b.Bytes()), nil 34 | } 35 | 36 | // IsBuiltin checks if the name is builtin 37 | func IsBuiltin(name string) bool { 38 | if builtins[name] == "" { 39 | return false 40 | } 41 | return true 42 | } 43 | -------------------------------------------------------------------------------- /internal/livereload/livereload.go: -------------------------------------------------------------------------------- 1 | package livereload 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "sync" 7 | 8 | "github.com/julienschmidt/httprouter" 9 | ) 10 | 11 | var _ http.Handler = (*Server)(nil) 12 | 13 | // New livereload server 14 | func New(port int) *Server { 15 | srv := &Server{} 16 | 17 | router := httprouter.New() 18 | srv.router = router 19 | router.HandlerFunc("GET", "/livereload.js", srv.serveJS) 20 | router.HandlerFunc("GET", "/livereload", srv.serveWS) 21 | 22 | srv.mu = &sync.RWMutex{} 23 | srv.conns = map[*conn]struct{}{} 24 | srv.js = fmt.Sprintf(js, port) 25 | 26 | return srv 27 | 28 | } 29 | 30 | // Server struct 31 | type Server struct { 32 | router *httprouter.Router 33 | js string 34 | 35 | // protects the connections 36 | mu *sync.RWMutex 37 | conns map[*conn]struct{} 38 | } 39 | 40 | // Reload a file 41 | func (s *Server) Reload(file string) error { 42 | s.mu.RLock() 43 | defer s.mu.RUnlock() 44 | 45 | for conn := range s.conns { 46 | conn.Reload(file) 47 | } 48 | 49 | return nil 50 | } 51 | 52 | // ServeHTTP implements the http.Handler interface 53 | func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 54 | s.router.ServeHTTP(w, r) 55 | } 56 | 57 | // serveJS serves the client 58 | func (s *Server) serveJS(w http.ResponseWriter, r *http.Request) { 59 | w.Header().Set("Content-Type", "application/javascript") 60 | w.Write([]byte(s.js)) 61 | } 62 | 63 | // serveWS serves the websocket 64 | func (s *Server) serveWS(w http.ResponseWriter, r *http.Request) { 65 | conn, err := upgrade(r.Context(), w, r) 66 | if err != nil { 67 | http.Error(w, err.Error(), 500) 68 | return 69 | } 70 | 71 | // add the connection and defer it's removal 72 | s.mu.Lock() 73 | s.conns[conn] = struct{}{} 74 | s.mu.Unlock() 75 | defer func() { 76 | s.mu.Lock() 77 | delete(s.conns, conn) 78 | s.mu.Unlock() 79 | }() 80 | 81 | // wait until the connection errors out 82 | if err := conn.Wait(); err != nil { 83 | return 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /internal/mains/testdata/one/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() {} 4 | -------------------------------------------------------------------------------- /internal/mains/testdata/two/a/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() {} 4 | -------------------------------------------------------------------------------- /internal/mains/testdata/two/b/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() {} 4 | -------------------------------------------------------------------------------- /internal/regenerator/co.js: -------------------------------------------------------------------------------- 1 | var co = require('co') 2 | console.log(co) 3 | 4 | co( 5 | function*() { 6 | yield sleep(5000) 7 | return 'hi' 8 | }, 9 | function(err, msg) { 10 | console.log('all done!', msg) 11 | } 12 | ) 13 | 14 | function sleep(ms) { 15 | return function(fn) { 16 | setTimeout(fn, ms) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/regenerator/es6.js: -------------------------------------------------------------------------------- 1 | async function main(ms) { 2 | await sleep(ms) 3 | } 4 | 5 | function sleep(ms) { 6 | return new Promise(function(resolve) { 7 | setTimeout(resolve, ms) 8 | }) 9 | } 10 | 11 | main(10000).then(function() { 12 | console.log('all done') 13 | }) 14 | -------------------------------------------------------------------------------- /internal/regenerator/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "regenerator", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "co": { 8 | "version": "1.5.2", 9 | "resolved": "https://registry.npmjs.org/co/-/co-1.5.2.tgz", 10 | "integrity": "sha1-WMbYTmZPyC42PRLzavvz3I0LSHk=" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /internal/regenerator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "regenerator", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "co.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "co": "^1.5.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/testutil/copy.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // Copy recursively copies a directory tree, attempting to preserve permissions. 12 | // Source directory must exist, destination directory must *not* exist. 13 | // Symlinks are ignored and skipped. 14 | func Copy(src string, dst string) (err error) { 15 | src = filepath.Clean(src) 16 | dst = filepath.Clean(dst) 17 | 18 | si, err := os.Stat(src) 19 | if err != nil { 20 | return err 21 | } 22 | if !si.IsDir() { 23 | return fmt.Errorf("source is not a directory") 24 | } 25 | 26 | _, err = os.Stat(dst) 27 | if err != nil && !os.IsNotExist(err) { 28 | return 29 | } 30 | if err == nil { 31 | return fmt.Errorf("destination already exists") 32 | } 33 | 34 | err = os.MkdirAll(dst, si.Mode()) 35 | if err != nil { 36 | return 37 | } 38 | 39 | entries, err := ioutil.ReadDir(src) 40 | if err != nil { 41 | return 42 | } 43 | 44 | for _, entry := range entries { 45 | srcPath := filepath.Join(src, entry.Name()) 46 | dstPath := filepath.Join(dst, entry.Name()) 47 | 48 | if entry.IsDir() { 49 | err = Copy(srcPath, dstPath) 50 | if err != nil { 51 | return 52 | } 53 | } else { 54 | // Skip symlinks. 55 | if entry.Mode()&os.ModeSymlink != 0 { 56 | continue 57 | } 58 | 59 | err = copyFile(srcPath, dstPath) 60 | if err != nil { 61 | return 62 | } 63 | } 64 | } 65 | 66 | return 67 | } 68 | 69 | // CopyFile copies the contents of the file named src to the file named 70 | // by dst. The file will be created if it does not already exist. If the 71 | // destination file exists, all it's contents will be replaced by the contents 72 | // of the source file. The file mode will be copied from the source and 73 | // the copied data is synced/flushed to stable storage. 74 | func copyFile(src, dst string) (err error) { 75 | in, err := os.Open(src) 76 | if err != nil { 77 | return 78 | } 79 | defer in.Close() 80 | 81 | out, err := os.Create(dst) 82 | if err != nil { 83 | return 84 | } 85 | defer func() { 86 | if e := out.Close(); e != nil { 87 | err = e 88 | } 89 | }() 90 | 91 | _, err = io.Copy(out, in) 92 | if err != nil { 93 | return 94 | } 95 | 96 | err = out.Sync() 97 | if err != nil { 98 | return 99 | } 100 | 101 | si, err := os.Stat(src) 102 | if err != nil { 103 | return 104 | } 105 | err = os.Chmod(dst, si.Mode()) 106 | if err != nil { 107 | return 108 | } 109 | 110 | return 111 | } 112 | -------------------------------------------------------------------------------- /internal/testutil/format.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | 7 | "github.com/kr/pretty" 8 | "github.com/sergi/go-diff/diffmatchpatch" 9 | ) 10 | 11 | // Format fn 12 | func Format(expected, actual interface{}) string { 13 | e := pretty.Sprint(expected) 14 | a := pretty.Sprint(actual) 15 | 16 | dmp := diffmatchpatch.New() 17 | diffs := dmp.DiffMain(e, a, false) 18 | 19 | var buf bytes.Buffer 20 | for _, diff := range diffs { 21 | switch diff.Type { 22 | case diffmatchpatch.DiffInsert: 23 | buf.WriteString("\x1b[102m\x1b[30m") 24 | buf.WriteString(diff.Text) 25 | buf.WriteString("\x1b[0m") 26 | case diffmatchpatch.DiffDelete: 27 | buf.WriteString("\x1b[101m\x1b[30m") 28 | buf.WriteString(diff.Text) 29 | buf.WriteString("\x1b[0m") 30 | case diffmatchpatch.DiffEqual: 31 | buf.WriteString(diff.Text) 32 | } 33 | } 34 | 35 | result := buf.String() 36 | result = strings.Replace(result, "\\n", "\n", -1) 37 | result = strings.Replace(result, "\\t", "\t", -1) 38 | return result 39 | } 40 | -------------------------------------------------------------------------------- /internal/typewriter/typewriter.go: -------------------------------------------------------------------------------- 1 | package typewriter 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // Type fn 9 | func Type(text string, speed time.Duration) { 10 | sleep := int(speed) / len(text) 11 | for _, ch := range text { 12 | fmt.Printf(string(ch)) 13 | time.Sleep(time.Duration(sleep)) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Version of Joy. This will be changed via compiler flags 4 | var Version = "master" 5 | -------------------------------------------------------------------------------- /macro/raw.go: -------------------------------------------------------------------------------- 1 | package macro 2 | 3 | // Raw inlines a JS expression 4 | // js:"Raw,omit" 5 | func Raw(src string, vars ...interface{}) interface{} { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /macro/rawfile.go: -------------------------------------------------------------------------------- 1 | package macro 2 | 3 | // File inlines a JS file 4 | // js:"File,omit" 5 | func File(filepath string) interface{} { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /macro/rewrite.go: -------------------------------------------------------------------------------- 1 | package macro 2 | 3 | // Rewrite an expression replacing the variables as needed 4 | // js:"Rewrite,omit" 5 | func Rewrite(expr string, variables ...interface{}) interface{} { 6 | return "" 7 | } 8 | -------------------------------------------------------------------------------- /macro/runtime.go: -------------------------------------------------------------------------------- 1 | package macro 2 | 3 | // Runtime fn 4 | // js:"Runtime,omit" 5 | func Runtime(defs ...string) interface{} { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /stdlib/encoding/json/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // Marshal a struct into JSON 6 | func Marshal(v interface{}) ([]byte, error) { 7 | macro.Rewrite(`(function(v) { 8 | try { return [ JSON.stringify(v), null ] } 9 | catch (e) { return [ null, e ] } 10 | })($1)`, v) 11 | return nil, nil 12 | } 13 | 14 | // Unmarshal a struct into JSON 15 | // TODO: create & use object.assign runtime 16 | func Unmarshal(data []byte, v interface{}) error { 17 | macro.Rewrite(`(function(data, v) { 18 | try { 19 | var o = JSON.parse(data) 20 | for (var k in o) v[k] = o[k] 21 | return null 22 | } catch (e) { return e } 23 | })($1, $2)`, data, v) 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /stdlib/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // New error from text 6 | func New(text string) error { 7 | macro.Rewrite("new Error($1)", text) 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /stdlib/fmt/fmt.go: -------------------------------------------------------------------------------- 1 | package fmt 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // Println formats using the default formats for its operands and writes to standard output. 6 | // Spaces are always added between operands and a newline is appended. 7 | // It returns the number of bytes written and any write error encountered. 8 | func Println(a ...interface{}) (n int, err error) { 9 | macro.Rewrite("console.log.apply(console.log, $1)", a...) 10 | return 0, nil 11 | } 12 | 13 | // Printf formats according to a format specifier and writes to standard output. 14 | // It returns the number of bytes written and any write error encountered. 15 | // 16 | // NOTE: This differs slightly from the Go implementation because there's no console 17 | // API for printing to the console on the same line. Should be fine though. 18 | func Printf(format string, a ...interface{}) (n int, err error) { 19 | macro.Rewrite("console.log.apply(console.log, [$1].concat($2))", format, a) 20 | return 0, nil 21 | } 22 | -------------------------------------------------------------------------------- /stdlib/net/url/url.go: -------------------------------------------------------------------------------- 1 | package url 2 | -------------------------------------------------------------------------------- /stdlib/strconv/strconv.go: -------------------------------------------------------------------------------- 1 | package strconv 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // Itoa is shorthand for FormatInt(int64(i), 10). 6 | func Itoa(i int) string { 7 | macro.Rewrite("String($1)", i) 8 | return "" 9 | } 10 | -------------------------------------------------------------------------------- /stdlib/strings/strings.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | ) 6 | 7 | // Join concatenates the elements of a to create a single string. The separator string 8 | // sep is placed between elements in the resulting string. 9 | func Join(a []string, sep string) string { 10 | macro.Rewrite("$1.join($2)", a, sep) 11 | return "" 12 | } 13 | 14 | // ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case. 15 | func ToUpper(s string) string { 16 | macro.Rewrite("$1.toUpperCase()", s) 17 | return "" 18 | } 19 | 20 | // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. 21 | func ToLower(s string) string { 22 | macro.Rewrite("$1.toLowerCase()", s) 23 | return "" 24 | } 25 | -------------------------------------------------------------------------------- /stdlib/time/time.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | ) 6 | 7 | // Time struct 8 | // type Time struct{} 9 | 10 | // A Duration represents the elapsed time between two instants 11 | // as an int64 nanosecond count. The representation limits the 12 | // largest representable duration to approximately 290 years. 13 | // type Duration int64 14 | 15 | // const ( 16 | // minDuration Duration = -1 << 63 17 | // maxDuration Duration = 1<<63 - 1 18 | // ) 19 | 20 | // Common durations. There is no definition for units of Day or larger 21 | // to avoid confusion across daylight savings time zone transitions. 22 | // 23 | // To count the number of units in a Duration, divide: 24 | // second := time.Second 25 | // fmt.Print(int64(second/time.Millisecond)) // prints 1000 26 | // 27 | // To convert an integer number of units to a Duration, multiply: 28 | // seconds := 10 29 | // fmt.Print(time.Duration(seconds)*time.Second) // prints 10s 30 | 31 | // Millisecond const 32 | var Millisecond = func() int { 33 | macro.Rewrite("1") 34 | return 1000 * 1000 * 1 35 | }() 36 | 37 | // Second const 38 | var Second = func() int { 39 | macro.Rewrite("1000") 40 | return 1000 * 1000 * 1000 * 1 41 | }() 42 | 43 | // Minute const 44 | var Minute = func() int { 45 | macro.Rewrite("60000") 46 | return 60 * 1000 * 1000 * 1000 * 1 47 | }() 48 | 49 | // Hour const 50 | var Hour = func() int { 51 | macro.Rewrite("3600000") 52 | return 60 * 60 * 1000 * 1000 * 1000 * 1 53 | }() 54 | 55 | // Sleep pauses the current goroutine for at least the duration d. 56 | // A negative or zero duration causes Sleep to return immediately. 57 | func Sleep(d int) { 58 | macro.Rewrite("await new Promise(function(resolve, reject) { setTimeout(resolve, $1) })", d) 59 | } 60 | -------------------------------------------------------------------------------- /testdata/01-hello-world/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/01-hello-world"] = (function() { 4 | function main () { 5 | console.log("hi world!") 6 | }; 7 | return { 8 | main: main 9 | }; 10 | })(); 11 | return pkg["github.com/matthewmueller/joy/testdata/01-hello-world"].main(); 12 | })() -------------------------------------------------------------------------------- /testdata/01-hello-world/expected.txt: -------------------------------------------------------------------------------- 1 | hi world! 2 | -------------------------------------------------------------------------------- /testdata/01-hello-world/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | println("hi world!") 5 | } 6 | -------------------------------------------------------------------------------- /testdata/02-variables/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/02-variables"] = (function() { 4 | function main () { 5 | var a = "a"; 6 | var b = "b", c = "c", d = "d"; 7 | var e = 3; 8 | var $f = (function() { 9 | return ["f", "g"]; 10 | })(), f = $f[0], g = $f[1]; 11 | var h = "h", i = "i", j = "j", k = "k"; 12 | console.log(a, b, c, d, e, f, g, h, i, j, k) 13 | }; 14 | return { 15 | main: main 16 | }; 17 | })(); 18 | return pkg["github.com/matthewmueller/joy/testdata/02-variables"].main(); 19 | })() -------------------------------------------------------------------------------- /testdata/02-variables/expected.txt: -------------------------------------------------------------------------------- 1 | a b c d 3 f g h i j k 2 | -------------------------------------------------------------------------------- /testdata/02-variables/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | a := "a" 5 | b, c, d := "b", "c", "d" 6 | 7 | var e = 3 8 | 9 | f, g := func() (string, string) { 10 | return "f", "g" 11 | }() 12 | 13 | var ( 14 | h = "h" 15 | i = "i" 16 | j, k = "j", "k" 17 | ) 18 | 19 | println(a, b, c, d, e, f, g, h, i, j, k) 20 | } 21 | -------------------------------------------------------------------------------- /testdata/03-function/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/03-function"] = (function() { 4 | function hello (name) { 5 | var out = ""; 6 | return "hello " + name; 7 | }; 8 | function main () { 9 | var greet = hello("anki"); 10 | console.log(greet) 11 | }; 12 | return { 13 | main: main 14 | }; 15 | })(); 16 | return pkg["github.com/matthewmueller/joy/testdata/03-function"].main(); 17 | })() -------------------------------------------------------------------------------- /testdata/03-function/expected.txt: -------------------------------------------------------------------------------- 1 | hello anki 2 | -------------------------------------------------------------------------------- /testdata/03-function/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | greet := hello("anki") 5 | println(greet) 6 | } 7 | 8 | func hello(name string) (out string) { 9 | return "hello " + name 10 | } 11 | -------------------------------------------------------------------------------- /testdata/04-if-elseif-else/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/04-if-elseif-else"] = (function() { 4 | function main () { 5 | var name = "anki"; 6 | var b = true; 7 | if (name == "matt" || name == "mark") { 8 | console.log("matt or mark") 9 | } else if (name == "anki" && b) { 10 | console.log("anki") 11 | } else if (b) { 12 | console.log("truthy") 13 | } else { 14 | console.log("something else") 15 | } 16 | }; 17 | return { 18 | main: main 19 | }; 20 | })(); 21 | return pkg["github.com/matthewmueller/joy/testdata/04-if-elseif-else"].main(); 22 | })() -------------------------------------------------------------------------------- /testdata/04-if-elseif-else/expected.txt: -------------------------------------------------------------------------------- 1 | anki 2 | -------------------------------------------------------------------------------- /testdata/04-if-elseif-else/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | name := "anki" 5 | b := true 6 | if name == "matt" || name == "mark" { 7 | println("matt or mark") 8 | } else if name == "anki" && b { 9 | println("anki") 10 | } else if b { 11 | println("truthy") 12 | } else { 13 | println("something else") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /testdata/05-for-loop/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/05-for-loop"] = (function() { 4 | function main () { 5 | for (var i = 0; i < 5; i++) { 6 | console.log(i) 7 | }; 8 | var j = 5; 9 | var k = 0; 10 | for (; ; ) { 11 | j-- 12 | if (j < 0) { 13 | break 14 | } else { 15 | k++ 16 | } 17 | }; 18 | console.log(k) 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/05-for-loop"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/05-for-loop/expected.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | -------------------------------------------------------------------------------- /testdata/05-for-loop/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | for i := 0; i < 5; i++ { 5 | println(i) 6 | } 7 | 8 | j := 5 9 | k := 0 10 | for { 11 | j-- 12 | if j < 0 { 13 | break 14 | } else { 15 | k++ 16 | } 17 | } 18 | println(k) 19 | } 20 | -------------------------------------------------------------------------------- /testdata/06-structs/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/06-structs"] = (function() { 4 | function Document (o) { 5 | o = o || {}; 6 | this.version = o.version || 0 7 | }; 8 | Document.prototype.GetElementByID = function(id) { 9 | return "#" + id; 10 | }; 11 | function New () { 12 | return new Document({ 13 | version: 1 14 | }); 15 | }; 16 | function main () { 17 | var doc = New(); 18 | var el = doc.GetElementByID.bind(doc)("canvas"); 19 | console.log("got element " + el) 20 | }; 21 | return { 22 | Document: Document, 23 | New: New, 24 | main: main 25 | }; 26 | })(); 27 | return pkg["github.com/matthewmueller/joy/testdata/06-structs"].main(); 28 | })() -------------------------------------------------------------------------------- /testdata/06-structs/expected.txt: -------------------------------------------------------------------------------- 1 | got element #canvas 2 | -------------------------------------------------------------------------------- /testdata/06-structs/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | doc := New() 5 | el := doc.GetElementByID("canvas") 6 | println("got element " + el) 7 | } 8 | 9 | // New document 10 | func New() Document { 11 | return Document{ 12 | Version: 1, 13 | } 14 | } 15 | 16 | // Document struct 17 | type Document struct { 18 | Version int `js:"version"` 19 | } 20 | 21 | // GetElementByID lookup element by it's id 22 | func (*Document) GetElementByID(id string) string { 23 | return "#" + id 24 | } 25 | -------------------------------------------------------------------------------- /testdata/07-struct-nested/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/07-struct-nested"] = (function() { 4 | function main () { 5 | var user = { 6 | Name: "Matt", 7 | Phone: { 8 | Type: "", 9 | Number: "1234511", 10 | Main: false 11 | } 12 | }; 13 | console.log(user.Phone.Number); 14 | user.Phone.Type = "CELL" 15 | }; 16 | return { 17 | main: main 18 | }; 19 | })(); 20 | return pkg["github.com/matthewmueller/joy/testdata/07-struct-nested"].main(); 21 | })() -------------------------------------------------------------------------------- /testdata/07-struct-nested/expected.txt: -------------------------------------------------------------------------------- 1 | 1234511 2 | -------------------------------------------------------------------------------- /testdata/07-struct-nested/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // User struct 4 | type User struct { 5 | Name string 6 | Phone Phone 7 | } 8 | 9 | // Phone struct 10 | type Phone struct { 11 | Type string 12 | Number string 13 | Main bool 14 | } 15 | 16 | func main() { 17 | user := User{ 18 | Name: "Matt", 19 | Phone: Phone{ 20 | Number: "1234511", 21 | Main: false, 22 | }, 23 | } 24 | 25 | println(user.Phone.Number) 26 | user.Phone.Type = "CELL" 27 | } 28 | -------------------------------------------------------------------------------- /testdata/08-struct-nested-array/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/08-struct-nested-array"] = (function() { 4 | function main () { 5 | var user = { 6 | FirstName: "Matt", 7 | LastName: "Mueller", 8 | Phones: [{ 9 | Type: "HOME", 10 | Number: "1234123", 11 | Main: false 12 | }, { 13 | Type: "CELL", 14 | Number: "0923123", 15 | Main: true 16 | }] 17 | }; 18 | console.log(user.FirstName + ": " + user.Phones[0].Number) 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/08-struct-nested-array"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/08-struct-nested-array/expected.txt: -------------------------------------------------------------------------------- 1 | Matt: 1234123 2 | -------------------------------------------------------------------------------- /testdata/08-struct-nested-array/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // User struct 4 | type User struct { 5 | FirstName string 6 | LastName string 7 | Phones []Phone 8 | } 9 | 10 | // Phone struct 11 | type Phone struct { 12 | Type string 13 | Number string 14 | Main bool 15 | } 16 | 17 | func main() { 18 | user := User{ 19 | FirstName: "Matt", 20 | LastName: "Mueller", 21 | Phones: []Phone{ 22 | Phone{ 23 | Type: "HOME", 24 | Number: "1234123", 25 | }, 26 | Phone{ 27 | Type: "CELL", 28 | Number: "0923123", 29 | Main: true, 30 | }, 31 | }, 32 | } 33 | 34 | println(user.FirstName + ": " + user.Phones[0].Number) 35 | } 36 | -------------------------------------------------------------------------------- /testdata/09-zero-values/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/09-zero-values"] = (function() { 4 | function main () { 5 | var user = { 6 | FirstName: "Tobi", 7 | LastName: "", 8 | Age: 0, 9 | Phone: { 10 | Type: "", 11 | Number: "" 12 | } 13 | }; 14 | console.log(user.FirstName); 15 | console.log(user.LastName); 16 | console.log(user.Age); 17 | console.log(user.Phone.Type); 18 | console.log(user.Phone.Number) 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/09-zero-values"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/09-zero-values/expected.txt: -------------------------------------------------------------------------------- 1 | Tobi 2 | 3 | 0 4 | 5 | -------------------------------------------------------------------------------- /testdata/09-zero-values/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // User struct 4 | type User struct { 5 | FirstName string 6 | LastName string 7 | Age int 8 | Phone Phone 9 | } 10 | 11 | // Phone struct 12 | type Phone struct { 13 | Type string 14 | Number string 15 | } 16 | 17 | func main() { 18 | user := User{FirstName: "Tobi"} 19 | println(user.FirstName) 20 | println(user.LastName) 21 | println(user.Age) 22 | println(user.Phone.Type) 23 | println(user.Phone.Number) 24 | } 25 | -------------------------------------------------------------------------------- /testdata/10-arrays/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/10-arrays"] = (function() { 4 | function main () { 5 | var names = ["matt", "tejas", "tj"]; 6 | console.log(names[1]) 7 | }; 8 | return { 9 | main: main 10 | }; 11 | })(); 12 | return pkg["github.com/matthewmueller/joy/testdata/10-arrays"].main(); 13 | })() -------------------------------------------------------------------------------- /testdata/10-arrays/expected.txt: -------------------------------------------------------------------------------- 1 | tejas 2 | -------------------------------------------------------------------------------- /testdata/10-arrays/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | names := []string{"matt", "tejas", "tj"} 5 | println(names[1]) 6 | } 7 | -------------------------------------------------------------------------------- /testdata/11-array-of-arrays/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/11-array-of-arrays"] = (function() { 4 | function main () { 5 | var names = [["matt"], ["tejas"], ["tj"]]; 6 | console.log(names[1][0]) 7 | }; 8 | return { 9 | main: main 10 | }; 11 | })(); 12 | return pkg["github.com/matthewmueller/joy/testdata/11-array-of-arrays"].main(); 13 | })() -------------------------------------------------------------------------------- /testdata/11-array-of-arrays/expected.txt: -------------------------------------------------------------------------------- 1 | tejas 2 | -------------------------------------------------------------------------------- /testdata/11-array-of-arrays/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | names := [][]string{ 5 | []string{"matt"}, 6 | []string{"tejas"}, 7 | []string{"tj"}, 8 | } 9 | println(names[1][0]) 10 | } 11 | -------------------------------------------------------------------------------- /testdata/12-maps/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/12-maps"] = (function() { 4 | function main () { 5 | var names = { 6 | "a": "a", 7 | "b": "b" 8 | }; 9 | var nested = { 10 | "a": { 11 | "a": "a" 12 | }, 13 | "b": { 14 | "b": "b" 15 | } 16 | }; 17 | console.log(names["a"]); 18 | console.log(nested["b"]["b"]) 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/12-maps"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/12-maps/expected.txt: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /testdata/12-maps/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | names := map[string]string{ 5 | "a": "a", 6 | "b": "b", 7 | } 8 | 9 | nested := map[string]map[string]string{ 10 | "a": map[string]string{ 11 | "a": "a", 12 | }, 13 | "b": map[string]string{ 14 | "b": "b", 15 | }, 16 | } 17 | 18 | println(names["a"]) 19 | println(nested["b"]["b"]) 20 | } 21 | -------------------------------------------------------------------------------- /testdata/13-simple-raw/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/13-simple-raw"] = (function() { 4 | function main () { 5 | var x = 15; 6 | x += 5; 7 | console.log(x) 8 | }; 9 | return { 10 | main: main 11 | }; 12 | })(); 13 | return pkg["github.com/matthewmueller/joy/testdata/13-simple-raw"].main(); 14 | })() -------------------------------------------------------------------------------- /testdata/13-simple-raw/expected.txt: -------------------------------------------------------------------------------- 1 | 20 -------------------------------------------------------------------------------- /testdata/13-simple-raw/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | func main() { 6 | x := 15 7 | macro.Raw("x += 5") 8 | println(x) 9 | } 10 | -------------------------------------------------------------------------------- /testdata/14-empty-vars/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/14-empty-vars"] = (function() { 4 | function main () { 5 | var a = [], b = "hey"; 6 | ; 7 | console.log(a); 8 | console.log(b) 9 | }; 10 | return { 11 | main: main 12 | }; 13 | })(); 14 | return pkg["github.com/matthewmueller/joy/testdata/14-empty-vars"].main(); 15 | })() -------------------------------------------------------------------------------- /testdata/14-empty-vars/expected.txt: -------------------------------------------------------------------------------- 1 | [] 2 | hey 3 | -------------------------------------------------------------------------------- /testdata/14-empty-vars/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var ( 5 | a []string 6 | b = "hey" 7 | ) 8 | 9 | _, _ = a, b 10 | println(a) 11 | println(b) 12 | } 13 | -------------------------------------------------------------------------------- /testdata/15-append/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/15-append"] = (function() { 4 | function main () { 5 | var nodes = []; 6 | nodes = nodes.concat({ 7 | NodeName: "div" 8 | }, { 9 | NodeName: "strong" 10 | }); 11 | console.log(nodes[0].NodeName); 12 | console.log(nodes[1].NodeName) 13 | }; 14 | return { 15 | main: main 16 | }; 17 | })(); 18 | return pkg["github.com/matthewmueller/joy/testdata/15-append"].main(); 19 | })() -------------------------------------------------------------------------------- /testdata/15-append/expected.txt: -------------------------------------------------------------------------------- 1 | div 2 | strong 3 | -------------------------------------------------------------------------------- /testdata/15-append/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Node struct 4 | type Node struct { 5 | NodeName string 6 | } 7 | 8 | func main() { 9 | var nodes []Node 10 | 11 | nodes = append(nodes, Node{ 12 | NodeName: "div", 13 | }, Node{ 14 | NodeName: "strong", 15 | }) 16 | 17 | println(nodes[0].NodeName) 18 | println(nodes[1].NodeName) 19 | } 20 | -------------------------------------------------------------------------------- /testdata/16-range/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/16-range"] = (function() { 4 | function main () { 5 | var nodes = []; 6 | nodes = nodes.concat({ 7 | NodeName: "div" 8 | }, { 9 | NodeName: "strong" 10 | }); 11 | for (var i = 0, node; i < nodes.length; i++) { 12 | var node = nodes[i] 13 | console.log(i) 14 | console.log(node.NodeName) 15 | }; 16 | for (var i = 0; i < nodes.length; i++) { 17 | console.log(i) 18 | } 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/16-range"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/16-range/expected.txt: -------------------------------------------------------------------------------- 1 | 0 2 | div 3 | 1 4 | strong 5 | 0 6 | 1 7 | -------------------------------------------------------------------------------- /testdata/16-range/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Node struct 4 | type Node struct { 5 | NodeName string 6 | } 7 | 8 | func main() { 9 | var nodes []Node 10 | 11 | nodes = append(nodes, Node{ 12 | NodeName: "div", 13 | }, Node{ 14 | NodeName: "strong", 15 | }) 16 | 17 | for i, node := range nodes { 18 | println(i) 19 | println(node.NodeName) 20 | } 21 | 22 | for i := range nodes { 23 | println(i) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /testdata/20-anonymous-funcs/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/20-anonymous-funcs"] = (function() { 4 | function main () { 5 | (function() { 6 | console.log("hi") 7 | })() 8 | }; 9 | return { 10 | main: main 11 | }; 12 | })(); 13 | return pkg["github.com/matthewmueller/joy/testdata/20-anonymous-funcs"].main(); 14 | })() -------------------------------------------------------------------------------- /testdata/20-anonymous-funcs/expected.txt: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /testdata/20-anonymous-funcs/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | func() { 5 | println("hi") 6 | }() 7 | } 8 | -------------------------------------------------------------------------------- /testdata/21-packages/deepdep/deepdep.go: -------------------------------------------------------------------------------- 1 | package deepdep 2 | 3 | // Dep struct 4 | type Dep struct { 5 | deep string 6 | } 7 | 8 | // New fn 9 | func New() *Dep { 10 | return &Dep{ 11 | deep: "deep", 12 | } 13 | } 14 | 15 | func (d *Dep) String() string { 16 | return d.deep 17 | } 18 | -------------------------------------------------------------------------------- /testdata/21-packages/dep/another.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/21-packages/deepdep" 5 | ) 6 | 7 | // A var 8 | var A = "A" 9 | 10 | // B const 11 | // const B = "B" 12 | 13 | // C const 14 | var C = "C" 15 | 16 | func another() string { 17 | return "another" 18 | } 19 | 20 | // Another fn 21 | func Another() string { 22 | deep := deepdep.New() 23 | return "exported another " + deep.String() 24 | } 25 | 26 | // Unused fn 27 | func Unused() string { 28 | return "i'm not used" 29 | } 30 | -------------------------------------------------------------------------------- /testdata/21-packages/dep/dep.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | 4 | // Dep fn 5 | func Dep(dep string) string { 6 | return dep + " " + another() 7 | } 8 | 9 | -------------------------------------------------------------------------------- /testdata/21-packages/one/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/21-packages/dep"] = (function() { 4 | function another () { 5 | return "another"; 6 | }; 7 | function Dep (dep) { 8 | return dep + " " + another(); 9 | }; 10 | return { 11 | Dep: Dep 12 | }; 13 | })(); 14 | pkg["github.com/matthewmueller/joy/testdata/21-packages/one"] = (function() { 15 | var zarg = pkg["github.com/matthewmueller/joy/testdata/21-packages/dep"]; 16 | function main () { 17 | var t = { 18 | String: "hi" 19 | }; 20 | console.log(zarg.Dep("one") + t.String) 21 | }; 22 | return { 23 | main: main 24 | }; 25 | })(); 26 | return pkg["github.com/matthewmueller/joy/testdata/21-packages/one"].main(); 27 | })() -------------------------------------------------------------------------------- /testdata/21-packages/one/expected.txt: -------------------------------------------------------------------------------- 1 | one anotherhi 2 | -------------------------------------------------------------------------------- /testdata/21-packages/one/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | zarg "github.com/matthewmueller/joy/testdata/21-packages/dep" 5 | ) 6 | 7 | type test struct { 8 | String string 9 | } 10 | 11 | func main() { 12 | t := test{String: "hi"} 13 | println(zarg.Dep("one") + t.String) 14 | } 15 | -------------------------------------------------------------------------------- /testdata/21-packages/two/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/21-packages/deepdep"] = (function() { 4 | function Dep (o) { 5 | o = o || {}; 6 | this.deep = o.deep || "" 7 | }; 8 | function New () { 9 | return new Dep({ 10 | deep: "deep" 11 | }); 12 | }; 13 | Dep.prototype.String = function() { 14 | var d = this; 15 | return d.deep; 16 | }; 17 | return { 18 | Dep: Dep, 19 | New: New 20 | }; 21 | })(); 22 | pkg["github.com/matthewmueller/joy/testdata/21-packages/dep"] = (function() { 23 | var deepdep = pkg["github.com/matthewmueller/joy/testdata/21-packages/deepdep"]; 24 | var A = "A"; 25 | function Another () { 26 | var deep = deepdep.New(); 27 | return "exported another " + deep.String(); 28 | }; 29 | function another () { 30 | return "another"; 31 | }; 32 | function Dep (dep) { 33 | return dep + " " + another(); 34 | }; 35 | return { 36 | A: A, 37 | Another: Another, 38 | Dep: Dep 39 | }; 40 | })(); 41 | pkg["github.com/matthewmueller/joy/testdata/21-packages/two"] = (function() { 42 | var dep = pkg["github.com/matthewmueller/joy/testdata/21-packages/dep"]; 43 | function main () { 44 | console.log(dep.Dep("two") + dep.A + dep.Another()) 45 | }; 46 | return { 47 | main: main 48 | }; 49 | })(); 50 | return pkg["github.com/matthewmueller/joy/testdata/21-packages/two"].main(); 51 | })() -------------------------------------------------------------------------------- /testdata/21-packages/two/expected.txt: -------------------------------------------------------------------------------- 1 | two anotherAexported another deep 2 | -------------------------------------------------------------------------------- /testdata/21-packages/two/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/21-packages/dep" 5 | ) 6 | 7 | func main() { 8 | println(dep.Dep("two") + dep.A + dep.Another()) 9 | } 10 | -------------------------------------------------------------------------------- /testdata/22-basic-bindings/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/22-basic-bindings"] = (function() { 4 | function definition (a, b) { 5 | var arr = []; 6 | var err = null; 7 | arr = arr.concat("5"); 8 | 9 | var c = String(parseInt(a) + b) 10 | arr.push(c) 11 | ; 12 | arr = arr.concat("9"); 13 | return [arr, err]; 14 | }; 15 | function main () { 16 | var $arr = definition("5", 3), arr = $arr[0], err = $arr[1]; 17 | if (err != null) { 18 | console.log(err) 19 | return ; 20 | }; 21 | console.log(arr) 22 | }; 23 | return { 24 | main: main 25 | }; 26 | })(); 27 | return pkg["github.com/matthewmueller/joy/testdata/22-basic-bindings"].main(); 28 | })() -------------------------------------------------------------------------------- /testdata/22-basic-bindings/expected.txt: -------------------------------------------------------------------------------- 1 | [ '5', '8', '9' ] 2 | -------------------------------------------------------------------------------- /testdata/22-basic-bindings/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // definition 6 | func definition(a string, b int) ([]string, error) { 7 | var arr []string 8 | var err error 9 | arr = append(arr, "5") 10 | macro.Raw(` 11 | var c = String(parseInt(a) + b) 12 | arr.push(c) 13 | `) 14 | arr = append(arr, "9") 15 | return arr, err 16 | } 17 | 18 | func main() { 19 | arr, err := definition("5", 3) 20 | if err != nil { 21 | println(err) 22 | return 23 | } 24 | println(arr) 25 | } 26 | -------------------------------------------------------------------------------- /testdata/23-goroutine-basic/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/internal/runtime"] = (function() { 4 | function Chan (o) { 5 | o = o || {}; 6 | this.capacity = o.capacity || 0; 7 | this.values = o.values || []; 8 | this.sends = o.sends || []; 9 | this.recvs = o.recvs || []; 10 | this.closed = o.closed || false 11 | }; 12 | function Channel (capacity) { 13 | return new Chan({ 14 | capacity: capacity 15 | }); 16 | }; 17 | function Deferred () { 18 | 19 | if (!(this instanceof Deferred)) return new Deferred() 20 | var self = this 21 | 22 | var p = new Promise(function(resolve, reject) { 23 | self.resolve = resolve 24 | self.reject = reject 25 | }) 26 | 27 | self.then = p.then.bind(p) 28 | self.catch = p.catch.bind(p) 29 | ; 30 | return self; 31 | }; 32 | Chan.prototype.Recv = function() { 33 | var c = this; 34 | if (c.values.length > 0) { 35 | var value = c.values[0] 36 | 37 | c.values = c.values.slice(1) 38 | return Promise.resolve(value); 39 | }; 40 | if (c.sends.length > 0) { 41 | var send = c.sends[0] 42 | 43 | c.sends = c.sends.slice(1) 44 | if (c.closed) { 45 | send.promise.reject(new Error('send on closed channel')) 46 | return Promise.resolve(); 47 | } 48 | send.promise.resolve() 49 | return Promise.resolve(send.value); 50 | }; 51 | if (c.closed) { 52 | return Promise.resolve(); 53 | }; 54 | var promise = Deferred(); 55 | c.recvs = c.recvs.concat(promise); 56 | return promise; 57 | }; 58 | Chan.prototype.Send = function(value) { 59 | var c = this; 60 | if (c.closed) { 61 | return Promise.reject(new Error('send on a closed channel')); 62 | }; 63 | if (c.recvs.length > 0) { 64 | var recv = c.recvs[0] 65 | 66 | c.recvs = c.recvs.slice(1) 67 | recv.resolve(value) 68 | return Promise.resolve(); 69 | }; 70 | if (c.values.length < c.capacity) { 71 | c.values = c.values.concat(value) 72 | return Promise.resolve(); 73 | }; 74 | var promise = Deferred(); 75 | c.sends = c.sends.concat({ 76 | value: value, 77 | promise: promise 78 | }); 79 | return promise; 80 | }; 81 | return { 82 | Chan: Chan, 83 | Channel: Channel, 84 | Deferred: Deferred 85 | }; 86 | })(); 87 | pkg["github.com/matthewmueller/joy/testdata/23-goroutine-basic"] = (function() { 88 | var runtime = pkg["github.com/matthewmueller/joy/internal/runtime"]; 89 | async function main () { 90 | var ch = new runtime.Channel(); 91 | (async function(msg) { 92 | await ch.Send(msg) 93 | })("hi"); 94 | console.log(await ch.Recv()) 95 | }; 96 | return { 97 | main: main 98 | }; 99 | })(); 100 | return pkg["github.com/matthewmueller/joy/testdata/23-goroutine-basic"].main(); 101 | })() -------------------------------------------------------------------------------- /testdata/23-goroutine-basic/expected.txt: -------------------------------------------------------------------------------- 1 | hi -------------------------------------------------------------------------------- /testdata/23-goroutine-basic/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan string) 5 | go func(msg string) { 6 | ch <- msg 7 | }("hi") 8 | 9 | println(<-ch) 10 | } 11 | -------------------------------------------------------------------------------- /testdata/24-interface-array/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/24-interface-array"] = (function() { 4 | function main () { 5 | var doc = { 6 | Fonts: [] 7 | }; 8 | console.log(doc.Fonts) 9 | }; 10 | return { 11 | main: main 12 | }; 13 | })(); 14 | return pkg["github.com/matthewmueller/joy/testdata/24-interface-array"].main(); 15 | })() -------------------------------------------------------------------------------- /testdata/24-interface-array/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type document struct { 4 | Fonts []interface{} 5 | } 6 | 7 | func main() { 8 | doc := document{} 9 | println(doc.Fonts) 10 | } 11 | -------------------------------------------------------------------------------- /testdata/25-external-file/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/25-external-file/fetch.js"] = (function() { 4 | return function fetch(url) { 5 | return 'Google' 6 | } 7 | 8 | })(); 9 | pkg["github.com/matthewmueller/joy/testdata/25-external-file"] = (function() { 10 | function call () { 11 | var fetch = pkg["github.com/matthewmueller/joy/testdata/25-external-file/fetch.js"]; 12 | return fetch; 13 | }; 14 | function main () { 15 | var fetch = call(); 16 | ; 17 | var res = fetch("http://google.com"); 18 | console.log(res) 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/25-external-file"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/25-external-file/expected.txt: -------------------------------------------------------------------------------- 1 | Google 2 | -------------------------------------------------------------------------------- /testdata/25-external-file/fetch.js: -------------------------------------------------------------------------------- 1 | return function fetch(url) { 2 | return 'Google' 3 | } 4 | -------------------------------------------------------------------------------- /testdata/25-external-file/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | func call() interface{} { 6 | fetch := macro.File(`./fetch.js`) 7 | return fetch 8 | } 9 | 10 | func main() { 11 | fetch := call() 12 | _ = fetch 13 | res := macro.Raw(`fetch("http://google.com")`) 14 | println(res) 15 | } 16 | -------------------------------------------------------------------------------- /testdata/26-errors/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/26-errors"] = (function() { 4 | function main () { 5 | var err = new Error("new error"); 6 | console.log(err.message); 7 | throw err 8 | }; 9 | return { 10 | main: main 11 | }; 12 | })(); 13 | return pkg["github.com/matthewmueller/joy/testdata/26-errors"].main(); 14 | })() -------------------------------------------------------------------------------- /testdata/26-errors/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | func main() { 6 | err := errors.New("new error") 7 | println(err.Error()) 8 | panic(err) 9 | } 10 | -------------------------------------------------------------------------------- /testdata/27-global-fetch/expected.txt: -------------------------------------------------------------------------------- 1 | matthewmueller - Matthew Mueller -------------------------------------------------------------------------------- /testdata/27-global-fetch/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/27-global-fetch/promise" 5 | ) 6 | 7 | // Fetch fn 8 | // js:"fetch,omit" 9 | func Fetch(url string, options *map[string]string) *promise.Promise { 10 | return &promise.Promise{} 11 | } 12 | 13 | // Response struct 14 | // js:"response,omit" 15 | type Response struct { 16 | responseText string 17 | } 18 | 19 | // JSON gets the json 20 | // js:"json" 21 | func (r *Response) JSON() string { 22 | return r.responseText 23 | } 24 | 25 | // Github struct 26 | // js:"github,omit" 27 | type Github struct { 28 | Login string `json:"login,omitempty" js:"login"` 29 | Name string `json:"name,omitempty" js:"name"` 30 | Bio string `json:"bio,omitempty" js:"bio"` 31 | } 32 | 33 | func main() { 34 | p := Fetch("https://api.github.com/users/matthewmueller", nil) 35 | ch := make(chan Github, 1) 36 | 37 | p.Then(func(v interface{}) interface{} { 38 | res := v.(Response) 39 | return res.JSON() 40 | }).Then(func(v interface{}) interface{} { 41 | ch <- v.(Github) 42 | return nil 43 | }).Catch(func(e error) interface{} { 44 | println(e) 45 | return nil 46 | }) 47 | 48 | gh := <-ch 49 | println(gh.Login, "-", gh.Name) 50 | } 51 | -------------------------------------------------------------------------------- /testdata/27-global-fetch/promise/promise.go: -------------------------------------------------------------------------------- 1 | package promise 2 | 3 | // Promise struct 4 | // js:"promise,omit" 5 | type Promise struct { 6 | value interface{} 7 | err error 8 | } 9 | 10 | // Resolve the promise 11 | // js:"resolve" 12 | func Resolve(v interface{}) *Promise { 13 | return &Promise{value: v} 14 | } 15 | 16 | // Reject the promise 17 | // js:"reject" 18 | func Reject(err error) *Promise { 19 | return &Promise{err: err} 20 | } 21 | 22 | // Then fn 23 | // js:"then" 24 | func (p *Promise) Then(func(v interface{}) interface{}) *Promise { 25 | return p 26 | } 27 | 28 | // Catch fn 29 | // js:"catch" 30 | func (p *Promise) Catch(func(err error) interface{}) *Promise { 31 | return p 32 | } 33 | -------------------------------------------------------------------------------- /testdata/28-if-simple-stmt/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/28-if-simple-stmt"] = (function() { 4 | function test () { 5 | return ["", new Error("oh noz")]; 6 | }; 7 | function main () { 8 | var $x = test(), x = $x[0], err = $x[1]; 9 | if (err != null) { 10 | console.log(err.message) 11 | } else { 12 | console.log(x) 13 | } 14 | }; 15 | return { 16 | main: main 17 | }; 18 | })(); 19 | return pkg["github.com/matthewmueller/joy/testdata/28-if-simple-stmt"].main(); 20 | })() -------------------------------------------------------------------------------- /testdata/28-if-simple-stmt/expected.txt: -------------------------------------------------------------------------------- 1 | oh noz 2 | -------------------------------------------------------------------------------- /testdata/28-if-simple-stmt/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | func main() { 6 | if x, err := test(); err != nil { 7 | println(err.Error()) 8 | } else { 9 | println(x) 10 | } 11 | } 12 | 13 | func test() (string, error) { 14 | return "", errors.New("oh noz") 15 | } 16 | -------------------------------------------------------------------------------- /testdata/30-json-marshal/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/30-json-marshal"] = (function() { 4 | function main () { 5 | var $bytes = (function(v) { 6 | try { return [ JSON.stringify(v), null ] } 7 | catch (e) { return [ null, e ] } 8 | })({ 9 | name: "Matt", 10 | age: 28 11 | }), bytes = $bytes[0], err = $bytes[1]; 12 | if (err != null) { 13 | throw err 14 | }; 15 | console.log(bytes) 16 | }; 17 | return { 18 | main: main 19 | }; 20 | })(); 21 | return pkg["github.com/matthewmueller/joy/testdata/30-json-marshal"].main(); 22 | })() -------------------------------------------------------------------------------- /testdata/30-json-marshal/expected.txt: -------------------------------------------------------------------------------- 1 | {"name":"Matt","age":28} 2 | -------------------------------------------------------------------------------- /testdata/30-json-marshal/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "encoding/json" 4 | 5 | // Person struct 6 | type Person struct { 7 | Name string `json:"name,omitempty" js:"name"` 8 | Age int `json:"age,omitempty" js:"age"` 9 | } 10 | 11 | func main() { 12 | bytes, err := json.Marshal(&Person{ 13 | Name: "Matt", 14 | Age: 28, 15 | }) 16 | if err != nil { 17 | panic(err) 18 | } 19 | println(bytes) 20 | } 21 | -------------------------------------------------------------------------------- /testdata/31-json-unmarshal/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/31-json-unmarshal"] = (function() { 4 | function follows (p) { 5 | console.log(p.name) 6 | }; 7 | function main () { 8 | var p = { 9 | name: "", 10 | age: 0 11 | }; 12 | var e = (function(data, v) { 13 | try { 14 | var o = JSON.parse(data) 15 | for (var k in o) v[k] = o[k] 16 | return null 17 | } catch (e) { return e } 18 | })("{\"name\":\"ma\\\"tt\",\"age\":28}", p); 19 | if (e != null) { 20 | throw e 21 | }; 22 | follows(p) 23 | }; 24 | return { 25 | main: main 26 | }; 27 | })(); 28 | return pkg["github.com/matthewmueller/joy/testdata/31-json-unmarshal"].main(); 29 | })() -------------------------------------------------------------------------------- /testdata/31-json-unmarshal/expected.txt: -------------------------------------------------------------------------------- 1 | ma"tt 2 | -------------------------------------------------------------------------------- /testdata/31-json-unmarshal/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "encoding/json" 4 | 5 | // Person struct 6 | type Person struct { 7 | Name string `json:"name,omitempty" js:"name"` 8 | Age int `json:"age,omitempty" js:"age"` 9 | } 10 | 11 | func main() { 12 | var p Person 13 | if e := json.Unmarshal([]byte(`{"name":"ma\"tt","age":28}`), &p); e != nil { 14 | panic(e) 15 | } 16 | follows(&p) 17 | } 18 | 19 | func follows(p *Person) { 20 | println(p.Name) 21 | } 22 | -------------------------------------------------------------------------------- /testdata/32-goroutine-deep/expected.txt: -------------------------------------------------------------------------------- 1 | hi 2 | another -------------------------------------------------------------------------------- /testdata/32-goroutine-deep/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan string) 5 | go func() { 6 | func() { 7 | go test(ch) 8 | }() 9 | }() 10 | 11 | println(<-ch) 12 | println(another()) 13 | } 14 | 15 | func test(ch chan string) { 16 | test2(ch) 17 | } 18 | 19 | func test2(ch chan string) { 20 | ch <- "hi" 21 | } 22 | 23 | func another() string { 24 | return "another" 25 | } 26 | -------------------------------------------------------------------------------- /testdata/33-strings-join/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/33-strings-join"] = (function() { 4 | function main () { 5 | var a = ["1", "2", "3", "4"]; 6 | console.log(a.join(" ")) 7 | }; 8 | return { 9 | main: main 10 | }; 11 | })(); 12 | return pkg["github.com/matthewmueller/joy/testdata/33-strings-join"].main(); 13 | })() -------------------------------------------------------------------------------- /testdata/33-strings-join/expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 3 4 -------------------------------------------------------------------------------- /testdata/33-strings-join/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "strings" 4 | 5 | func main() { 6 | a := []string{"1", "2", "3", "4"} 7 | println(strings.Join(a, " ")) 8 | } 9 | -------------------------------------------------------------------------------- /testdata/34-keyless-fields/dep/dep/dep.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | // Settings struct 4 | type Settings struct { 5 | Place string `js:"place"` 6 | } 7 | -------------------------------------------------------------------------------- /testdata/34-keyless-fields/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/34-keyless-fields"] = (function() { 4 | function main () { 5 | var settings = { 6 | place: "USA" 7 | }; 8 | var settings2 = { 9 | place: "EUROPE" 10 | }; 11 | var phone = { 12 | Type: "Android", 13 | number: "8674205" 14 | }; 15 | var age = 28; 16 | var u = { 17 | name: "matt", 18 | Age: age, 19 | phone: phone, 20 | Settings: settings, 21 | Settings2: settings2 22 | }; 23 | console.log(u.name, u.Age, u.phone.number, u.Settings.place, u.Settings2.place) 24 | }; 25 | return { 26 | main: main 27 | }; 28 | })(); 29 | return pkg["github.com/matthewmueller/joy/testdata/34-keyless-fields"].main(); 30 | })() -------------------------------------------------------------------------------- /testdata/34-keyless-fields/expected.txt: -------------------------------------------------------------------------------- 1 | matt 28 8674205 USA EUROPE -------------------------------------------------------------------------------- /testdata/34-keyless-fields/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/34-keyless-fields/dep/dep" 5 | ) 6 | 7 | // Phone struct 8 | type Phone struct { 9 | Type string 10 | Number string `js:"number"` 11 | } 12 | 13 | // User struct 14 | type User struct { 15 | Name string `js:"name"` 16 | Age int 17 | Phone *Phone `js:"phone"` 18 | *dep.Settings 19 | Settings2 *dep.Settings 20 | } 21 | 22 | func main() { 23 | settings := dep.Settings{Place: "USA"} 24 | settings2 := dep.Settings{Place: "EUROPE"} 25 | phone := Phone{"Android", "8674205"} 26 | age := 28 27 | u := User{"matt", age, &phone, &settings, &settings2} 28 | println(u.Name, u.Age, u.Phone.Number, u.Settings.Place, u.Settings2.Place) 29 | } 30 | -------------------------------------------------------------------------------- /testdata/35-interfaces/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/35-interfaces"] = (function() { 4 | function component (o) { 5 | o = o || {}; 6 | this.component = o.component || "" 7 | }; 8 | function New (name) { 9 | return new component({ 10 | component: name 11 | }); 12 | }; 13 | component.prototype.String = function() { 14 | var c = this; 15 | return c.component; 16 | }; 17 | function main () { 18 | var c = New("awesomeness"); 19 | console.log(c.String()) 20 | }; 21 | return { 22 | New: New, 23 | main: main 24 | }; 25 | })(); 26 | return pkg["github.com/matthewmueller/joy/testdata/35-interfaces"].main(); 27 | })() -------------------------------------------------------------------------------- /testdata/35-interfaces/expected.txt: -------------------------------------------------------------------------------- 1 | awesomeness -------------------------------------------------------------------------------- /testdata/35-interfaces/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var _ Component = (*component)(nil) 4 | 5 | // Component interface 6 | type Component interface { 7 | String() string 8 | } 9 | 10 | type component struct { 11 | component string 12 | } 13 | 14 | func (c component) String() string { 15 | return c.component 16 | } 17 | 18 | // New component 19 | func New(name string) Component { 20 | return &component{name} 21 | } 22 | 23 | func main() { 24 | c := New("awesomeness") 25 | println(c.String()) 26 | } 27 | -------------------------------------------------------------------------------- /testdata/36-unused-interfaces/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/36-unused-interfaces"] = (function() { 4 | function component (o) { 5 | o = o || {}; 6 | this.component = o.component || "" 7 | }; 8 | function New (name) { 9 | return new component({ 10 | component: name 11 | }); 12 | }; 13 | component.prototype.String = function() { 14 | var c = this; 15 | return c.component; 16 | }; 17 | function main () { 18 | var c = New("awesomeness"); 19 | console.log(c.String()) 20 | }; 21 | return { 22 | New: New, 23 | main: main 24 | }; 25 | })(); 26 | return pkg["github.com/matthewmueller/joy/testdata/36-unused-interfaces"].main(); 27 | })() -------------------------------------------------------------------------------- /testdata/36-unused-interfaces/expected.txt: -------------------------------------------------------------------------------- 1 | awesomeness -------------------------------------------------------------------------------- /testdata/36-unused-interfaces/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var _ Component = (*component)(nil) 4 | 5 | // Component interface 6 | type Component interface { 7 | String() string 8 | } 9 | 10 | type component struct { 11 | component string 12 | } 13 | 14 | func (c component) String() string { 15 | return c.component 16 | } 17 | 18 | func (c component) another() string { 19 | return "another" 20 | } 21 | 22 | type blah struct { 23 | Blah string 24 | } 25 | 26 | func (b blah) String() string { 27 | return b.Blah 28 | } 29 | 30 | // New component 31 | func New(name string) Component { 32 | return &component{name} 33 | } 34 | 35 | func main() { 36 | c := New("awesomeness") 37 | println(c.String()) 38 | } 39 | -------------------------------------------------------------------------------- /testdata/37-deep-interfaces/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/37-deep-interfaces"] = (function() { 4 | vnode.prototype.String = function() { 5 | var v = this; 6 | return "earth"; 7 | }; 8 | function index (o) { 9 | o = o || {}; 10 | this.location = o.location || "" 11 | }; 12 | function vnode (o) { 13 | o = o || {}; 14 | this.c = o.c || null 15 | }; 16 | function newComponent (c) { 17 | return new vnode({ 18 | c: c 19 | }); 20 | }; 21 | function newIndex (location) { 22 | return newComponent(new index({ 23 | location: location 24 | })); 25 | }; 26 | function main () { 27 | var c = newIndex("mars"); 28 | console.log(c.String()) 29 | }; 30 | return { 31 | main: main 32 | }; 33 | })(); 34 | return pkg["github.com/matthewmueller/joy/testdata/37-deep-interfaces"].main(); 35 | })() -------------------------------------------------------------------------------- /testdata/37-deep-interfaces/expected.txt: -------------------------------------------------------------------------------- 1 | earth -------------------------------------------------------------------------------- /testdata/37-deep-interfaces/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // VNode interface 4 | type VNode interface { 5 | String() string 6 | } 7 | 8 | // Component interface 9 | type Component interface { 10 | Render() string 11 | } 12 | 13 | type vnode struct { 14 | c *Component 15 | } 16 | 17 | func (v *vnode) String() string { 18 | return "earth" 19 | } 20 | 21 | func newComponent(c Component) VNode { 22 | return &vnode{&c} 23 | } 24 | 25 | type index struct { 26 | location string 27 | } 28 | 29 | func (i *index) Render() string { 30 | return i.location 31 | } 32 | 33 | func newIndex(location string) VNode { 34 | return newComponent(&index{location}) 35 | } 36 | 37 | func main() { 38 | c := newIndex("mars") 39 | println(c.String()) 40 | } 41 | -------------------------------------------------------------------------------- /testdata/38-receiver-funcs/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/38-receiver-funcs"] = (function() { 4 | subthing.prototype.Name = function() { 5 | var s = this; 6 | return s.name; 7 | }; 8 | function subthing (o) { 9 | o = o || {}; 10 | this.name = o.name || "" 11 | }; 12 | function main () { 13 | var s = { 14 | subthing: new subthing({ 15 | name: "matt" 16 | }) 17 | }; 18 | console.log(s.subthing.Name.bind(s.subthing)()) 19 | }; 20 | return { 21 | main: main 22 | }; 23 | })(); 24 | return pkg["github.com/matthewmueller/joy/testdata/38-receiver-funcs"].main(); 25 | })() -------------------------------------------------------------------------------- /testdata/38-receiver-funcs/expected.txt: -------------------------------------------------------------------------------- 1 | matt -------------------------------------------------------------------------------- /testdata/38-receiver-funcs/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Subthing struct 4 | type subthing struct { 5 | name string 6 | } 7 | 8 | // Name string 9 | func (s *subthing) Name() string { 10 | return s.name 11 | } 12 | 13 | // Thing struct 14 | type Thing struct { 15 | *subthing 16 | } 17 | 18 | func main() { 19 | s := Thing{&subthing{"matt"}} 20 | println(s.subthing.Name()) 21 | } 22 | -------------------------------------------------------------------------------- /testdata/39-inner-interfaces/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/39-inner-interfaces"] = (function() { 4 | stringer.prototype.String = function() { 5 | var s = this; 6 | return s.value; 7 | }; 8 | Subthing.prototype.firstName = function() { 9 | var s = this; 10 | return s.first; 11 | }; 12 | Subthing.prototype.lastName = function() { 13 | var s = this; 14 | return s.last; 15 | }; 16 | Subthing.prototype.Name = function() { 17 | var s = this; 18 | return s.firstName.bind(s)().String() + " " + s.lastName.bind(s)().String(); 19 | }; 20 | function Subthing (o) { 21 | o = o || {}; 22 | this.first = o.first || null; 23 | this.last = o.last || null 24 | }; 25 | function stringer (o) { 26 | o = o || {}; 27 | this.value = o.value || "" 28 | }; 29 | function main () { 30 | var s = { 31 | Subthing: new Subthing({ 32 | first: new stringer({ 33 | value: "matt" 34 | }), 35 | last: new stringer({ 36 | value: "mueller" 37 | }) 38 | }) 39 | }; 40 | console.log(s.Subthing.Name.bind(s.Subthing)()) 41 | }; 42 | return { 43 | Subthing: Subthing, 44 | main: main 45 | }; 46 | })(); 47 | return pkg["github.com/matthewmueller/joy/testdata/39-inner-interfaces"].main(); 48 | })() -------------------------------------------------------------------------------- /testdata/39-inner-interfaces/expected.txt: -------------------------------------------------------------------------------- 1 | matt mueller -------------------------------------------------------------------------------- /testdata/39-inner-interfaces/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Stringer interface 4 | type Stringer interface { 5 | String() string 6 | } 7 | 8 | type stringer struct { 9 | value string 10 | } 11 | 12 | func (s *stringer) String() string { 13 | return s.value 14 | } 15 | 16 | // Subthing struct 17 | type Subthing struct { 18 | first Stringer 19 | last Stringer 20 | } 21 | 22 | func (s *Subthing) firstName() Stringer { 23 | return s.first 24 | } 25 | 26 | func (s *Subthing) lastName() Stringer { 27 | return s.last 28 | } 29 | 30 | // Name string 31 | func (s *Subthing) Name() string { 32 | return s.firstName().String() + " " + s.lastName().String() 33 | } 34 | 35 | // Thing struct 36 | type Thing struct { 37 | *Subthing 38 | } 39 | 40 | func main() { 41 | s := Thing{&Subthing{&stringer{"matt"}, &stringer{"mueller"}}} 42 | println(s.Subthing.Name()) 43 | } 44 | -------------------------------------------------------------------------------- /testdata/40-vnodes/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/40-vnodes"] = (function() { 4 | function component (o) { 5 | o = o || {}; 6 | this.component = o.component || null 7 | }; 8 | function NewComponent (com) { 9 | return new component({ 10 | component: com 11 | }); 12 | }; 13 | function Page (o) { 14 | o = o || {}; 15 | this.props = o.props || null 16 | }; 17 | function New (p) { 18 | return NewComponent(new Page({ 19 | props: p 20 | })); 21 | }; 22 | function Element (o) { 23 | o = o || {}; 24 | this.name = o.name || ""; 25 | this.kind = o.kind || 0; 26 | this.attributes = o.attributes || {}; 27 | this.children = o.children || [] 28 | }; 29 | function NewElement (tagName, attributes) { 30 | var children = Array.prototype.slice.call(arguments, 2); 31 | return new Element({ 32 | name: tagName, 33 | kind: 1, 34 | attributes: attributes, 35 | children: children 36 | }); 37 | }; 38 | Page.prototype.Render = function() { 39 | var p = this; 40 | return NewElement.apply(null, ["div"].concat(null)); 41 | }; 42 | component.prototype.String = function() { 43 | var c = this; 44 | return c.component.Render().String(); 45 | }; 46 | Element.prototype.String = function() { 47 | var e = this; 48 | var children = []; 49 | for (var _ = 0, child; _ < e.children.length; _++) { 50 | var child = e.children[_] 51 | children = children.concat(child.String()) 52 | }; 53 | return "<" + e.name + ">" + children.join("") + ""; 54 | }; 55 | function main () { 56 | var c = New({ 57 | Location: "mars" 58 | }); 59 | console.log(c.String()) 60 | }; 61 | return { 62 | NewComponent: NewComponent, 63 | Page: Page, 64 | New: New, 65 | Element: Element, 66 | NewElement: NewElement, 67 | main: main 68 | }; 69 | })(); 70 | return pkg["github.com/matthewmueller/joy/testdata/40-vnodes"].main(); 71 | })() -------------------------------------------------------------------------------- /testdata/40-vnodes/expected.txt: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /testdata/40-vnodes/index/index.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/40-vnodes/vnode" 5 | "github.com/matthewmueller/joy/testdata/40-vnodes/vnode/component" 6 | "github.com/matthewmueller/joy/testdata/40-vnodes/vnode/element" 7 | ) 8 | 9 | // Props struct 10 | type Props struct { 11 | Location string 12 | } 13 | 14 | // Page struct 15 | type Page struct { 16 | props *Props 17 | } 18 | 19 | // New fn 20 | func New(p *Props) vnode.VNode { 21 | return component.New(&Page{p}) 22 | } 23 | 24 | // ComponentWillMount fn 25 | func (p *Page) ComponentWillMount() { 26 | p.props.Location = "earth" 27 | } 28 | 29 | // Render the index page 30 | func (p *Page) Render() vnode.VNode { 31 | return element.New("div", nil) 32 | } 33 | -------------------------------------------------------------------------------- /testdata/40-vnodes/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "strings" 4 | 5 | // VNode interface 6 | type VNode interface { 7 | String() string 8 | } 9 | 10 | // NewElement node 11 | func NewElement(tagName string, attributes map[string]string, children ...VNode) *Element { 12 | return &Element{ 13 | name: tagName, 14 | kind: 1, 15 | attributes: attributes, 16 | children: children, 17 | } 18 | } 19 | 20 | // Element node 21 | type Element struct { 22 | name string 23 | kind int 24 | attributes map[string]string 25 | children []VNode 26 | } 27 | 28 | // String fn 29 | func (e *Element) String() string { 30 | var children []string 31 | for _, child := range e.children { 32 | children = append(children, child.String()) 33 | } 34 | return "<" + e.name + ">" + strings.Join(children, "") + "" 35 | } 36 | 37 | // Component interface 38 | type Component interface { 39 | Render() VNode 40 | } 41 | 42 | // NewComponent fn 43 | func NewComponent(com Component) VNode { 44 | return &component{ 45 | component: com, 46 | } 47 | } 48 | 49 | // component struct 50 | type component struct { 51 | component Component 52 | } 53 | 54 | // Render fn 55 | // func (c component) Render() VNode { 56 | // return c.component.Render() 57 | // } 58 | 59 | // String fn 60 | func (c component) String() string { 61 | return c.component.Render().String() 62 | } 63 | 64 | // Props struct 65 | type Props struct { 66 | Location string 67 | } 68 | 69 | // Page struct 70 | type Page struct { 71 | props *Props 72 | } 73 | 74 | // New fn 75 | func New(p *Props) VNode { 76 | return NewComponent(&Page{p}) 77 | } 78 | 79 | // Render the index page 80 | func (p Page) Render() VNode { 81 | return NewElement("div", nil) 82 | } 83 | 84 | func main() { 85 | c := New(&Props{ 86 | Location: "mars", 87 | }) 88 | 89 | println(c.String()) 90 | } 91 | -------------------------------------------------------------------------------- /testdata/40-vnodes/vnode/component/component.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "github.com/matthewmueller/joy/testdata/40-vnodes/vnode" 4 | 5 | // enforce VNode type 6 | var _ vnode.VNode = (*component)(nil) 7 | var _ Component = (*component)(nil) 8 | 9 | // Component interface 10 | type Component interface { 11 | Render() vnode.VNode 12 | } 13 | 14 | // func defaultProps interface{ 15 | // DefaultProps() 16 | // } 17 | 18 | // New fn 19 | func New(com Component) vnode.VNode { 20 | return &component{ 21 | component: com, 22 | } 23 | } 24 | 25 | // component struct 26 | type component struct { 27 | component Component 28 | } 29 | 30 | // Render fn 31 | func (c *component) Render() vnode.VNode { 32 | return c.component.Render() 33 | } 34 | 35 | // Name fn 36 | func (c *component) Name() string { 37 | return "#component" 38 | } 39 | 40 | // Type fn 41 | func (c *component) Type() int { 42 | return 0 43 | } 44 | 45 | // String fn 46 | func (c *component) String() string { 47 | willMount, ok := c.component.(componentWillMount) 48 | if ok { 49 | willMount.ComponentWillMount() 50 | } 51 | return c.Render().String() 52 | } 53 | 54 | // shouldComponentUpdate gets invoked before rendering when new props or state are being received. 55 | // This is not called for the initial render or when forceUpdate is used. 56 | type shouldComponentUpdate interface { 57 | ShouldComponentUpdate(next Component) bool 58 | } 59 | 60 | // componentWillUpdate gets invoked immediately before rendering when new props or state are being received. 61 | // This is not called for the initial render. 62 | type componentWillUpdate interface { 63 | ComponentWillUpdate(next Component) 64 | } 65 | 66 | // componentWillReceiveProps gets invoked when a component is receiving new props. 67 | // This method is not called for the initial render. 68 | type componentWillReceiveProps interface { 69 | ComponentWillReceiveProps(next Component) 70 | } 71 | 72 | // componentDidUpdate gets invoked immediately after the component's updates are flushed to the DOM. 73 | // This method is not called for the initial render. 74 | type componentDidUpdate interface { 75 | ComponentDidUpdate(prev Component) 76 | } 77 | 78 | // componentWillMount get invoked once, both on the client and server, immediately before the initial rendering occurs. 79 | type componentWillMount interface { 80 | ComponentWillMount() 81 | } 82 | 83 | // componentWillUnmount gets invoked immediately before a component is unmounted from the DOM. 84 | type componentWillUnmount interface { 85 | ComponentWillUnmount() 86 | } 87 | 88 | // componentDidMount gets invoked once, only on the client (not on the server), 89 | // immediately after the initial rendering occurs. 90 | type componentDidMount interface { 91 | ComponentDidMount() 92 | } 93 | -------------------------------------------------------------------------------- /testdata/40-vnodes/vnode/element/element.go: -------------------------------------------------------------------------------- 1 | package element 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/matthewmueller/joy/testdata/40-vnodes/vnode" 7 | ) 8 | 9 | // enforce VNode type 10 | var _ vnode.VNode = (*Element)(nil) 11 | var _ VElement = (*Element)(nil) 12 | 13 | // New element node 14 | func New(tagName string, attributes map[string]string, children ...vnode.VNode) *Element { 15 | return &Element{ 16 | name: tagName, 17 | kind: 1, 18 | attributes: attributes, 19 | children: children, 20 | } 21 | } 22 | 23 | // VElement interface 24 | type VElement interface { 25 | vnode.VNode 26 | Attributes() map[string]string 27 | Children() []vnode.VNode 28 | } 29 | 30 | // Element node 31 | type Element struct { 32 | name string 33 | kind int 34 | attributes map[string]string 35 | children []vnode.VNode 36 | } 37 | 38 | // Name fn 39 | func (e *Element) Name() string { 40 | return "" 41 | } 42 | 43 | // Type fn 44 | func (e *Element) Type() int { 45 | return e.kind 46 | } 47 | 48 | // Attributes fn 49 | func (e *Element) Attributes() map[string]string { 50 | return e.attributes 51 | } 52 | 53 | // Children fn 54 | func (e *Element) Children() []vnode.VNode { 55 | return e.children 56 | } 57 | 58 | // String fn 59 | func (e *Element) String() string { 60 | var children []string 61 | for _, child := range e.children { 62 | children = append(children, child.String()) 63 | } 64 | return "<" + e.name + ">" + strings.Join(children, "") + "" 65 | } 66 | -------------------------------------------------------------------------------- /testdata/40-vnodes/vnode/vnode.go: -------------------------------------------------------------------------------- 1 | package vnode 2 | 3 | // VNode interface 4 | type VNode interface { 5 | String() string 6 | Name() string 7 | Type() int 8 | } 9 | -------------------------------------------------------------------------------- /testdata/42-basic-rewrite/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/42-basic-rewrite"] = (function() { 4 | function main () { 5 | console.log("hi") 6 | }; 7 | return { 8 | main: main 9 | }; 10 | })(); 11 | return pkg["github.com/matthewmueller/joy/testdata/42-basic-rewrite"].main(); 12 | })() -------------------------------------------------------------------------------- /testdata/42-basic-rewrite/expected.txt: -------------------------------------------------------------------------------- 1 | hi -------------------------------------------------------------------------------- /testdata/42-basic-rewrite/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | func main() { 6 | test("hi") 7 | } 8 | 9 | func test(msg string) { 10 | macro.Rewrite("console.log($1)", msg) 11 | } 12 | -------------------------------------------------------------------------------- /testdata/43-var-function/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/43-var-function"] = (function() { 4 | function tester () { 5 | return "hi world"; 6 | }; 7 | var test = tester(); 8 | function main () { 9 | console.log(test) 10 | }; 11 | return { 12 | main: main 13 | }; 14 | })(); 15 | return pkg["github.com/matthewmueller/joy/testdata/43-var-function"].main(); 16 | })() -------------------------------------------------------------------------------- /testdata/43-var-function/expected.txt: -------------------------------------------------------------------------------- 1 | hi world -------------------------------------------------------------------------------- /testdata/43-var-function/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var test = tester() 4 | 5 | func main() { 6 | println(test) 7 | } 8 | 9 | func tester() string { 10 | return "hi world" 11 | } 12 | -------------------------------------------------------------------------------- /testdata/44-var-files/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/44-var-files/fn.js"] = (function() { 4 | return function hi(msg) { 5 | return msg + '!' 6 | } 7 | 8 | })(); 9 | pkg["github.com/matthewmueller/joy/testdata/44-var-files"] = (function() { 10 | var fn = pkg["github.com/matthewmueller/joy/testdata/44-var-files/fn.js"]; 11 | function main () { 12 | console.log(fn("hello world")) 13 | }; 14 | return { 15 | main: main 16 | }; 17 | })(); 18 | return pkg["github.com/matthewmueller/joy/testdata/44-var-files"].main(); 19 | })() -------------------------------------------------------------------------------- /testdata/44-var-files/expected.txt: -------------------------------------------------------------------------------- 1 | hello world! -------------------------------------------------------------------------------- /testdata/44-var-files/fn.js: -------------------------------------------------------------------------------- 1 | return function hi(msg) { 2 | return msg + '!' 3 | } 4 | -------------------------------------------------------------------------------- /testdata/44-var-files/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | ) 6 | 7 | var fn = macro.File("./fn.js") 8 | 9 | func main() { 10 | println(test("hello world")) 11 | } 12 | 13 | func test(msg string) string { 14 | macro.Rewrite("$1($2)", fn, msg) 15 | return "" 16 | } 17 | -------------------------------------------------------------------------------- /testdata/45-external-fetch/expected.txt: -------------------------------------------------------------------------------- 1 | .dotfiles -------------------------------------------------------------------------------- /testdata/45-external-fetch/fetch/fetch.go: -------------------------------------------------------------------------------- 1 | package fetch 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | ) 6 | 7 | // unfetch 8 | var unfetch = macro.File("./unfetch.js") 9 | 10 | // Options struct 11 | type Options struct { 12 | Method string 13 | } 14 | 15 | // Fetch fn 16 | func Fetch(url string, options *Options) (*Response, error) { 17 | macro.Raw(` 18 | try { 19 | var res = await unfetch(url, options) 20 | return [ res, null ] 21 | } catch (err) { 22 | return [ null, err ] 23 | } 24 | `, unfetch) 25 | return nil, nil 26 | } 27 | 28 | // Response struct 29 | // js:"response,omit" 30 | type Response struct { 31 | } 32 | 33 | // Text gets the textual representation 34 | // js:"text,async" 35 | func (r *Response) Text() []byte { 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /testdata/45-external-fetch/fetch/unfetch.js: -------------------------------------------------------------------------------- 1 | return function fetch(url, options) { 2 | options = options || {} 3 | return new Promise(function(resolve, reject) { 4 | var request = new XMLHttpRequest() 5 | 6 | request.open(options.method || 'get', url) 7 | 8 | for (var i in options.headers) { 9 | request.setRequestHeader(i, options.headers[i]) 10 | } 11 | 12 | request.withCredentials = options.credentials == 'include' 13 | 14 | request.onload = function() { 15 | resolve(response()) 16 | } 17 | 18 | request.onerror = reject 19 | 20 | request.send(options.body) 21 | 22 | function response() { 23 | var keys = [], 24 | all = [], 25 | headers = {}, 26 | header 27 | 28 | request 29 | .getAllResponseHeaders() 30 | .replace(/^(.*?):\s*([\s\S]*?)$/gm, function(m, key, value) { 31 | keys.push((key = key.toLowerCase())) 32 | all.push([key, value]) 33 | header = headers[key] 34 | headers[key] = header ? header + ',' + value : value 35 | }) 36 | 37 | return { 38 | ok: ((request.status / 200) | 0) == 1, // 200-299 39 | status: request.status, 40 | statusText: request.statusText, 41 | url: request.responseURL, 42 | clone: response, 43 | responseText: request.responseText, 44 | text: function() { 45 | return Promise.resolve(request.responseText) 46 | }, 47 | json: function() { 48 | return Promise.resolve(request.responseText).then(JSON.parse) 49 | }, 50 | blob: function() { 51 | return Promise.resolve(new Blob([request.response])) 52 | }, 53 | headers: { 54 | keys: function() { 55 | return keys 56 | }, 57 | entries: function() { 58 | return all 59 | }, 60 | get: function(n) { 61 | return headers[n.toLowerCase()] 62 | }, 63 | has: function(n) { 64 | return n.toLowerCase() in headers 65 | } 66 | } 67 | } 68 | } 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /testdata/45-external-fetch/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/matthewmueller/joy/testdata/45-external-fetch/fetch" 7 | ) 8 | 9 | func main() { 10 | test() 11 | } 12 | 13 | // Repo struct 14 | type Repo struct { 15 | Name string `js:"name"` 16 | } 17 | 18 | func test() { 19 | response, err := fetch.Fetch("https://api.github.com/users/matthewmueller/repos", &fetch.Options{ 20 | Method: "GET", 21 | }) 22 | if err != nil { 23 | println(err) 24 | return 25 | } 26 | 27 | var repos []Repo 28 | data := response.Text() 29 | if e := json.Unmarshal(data, &repos); e != nil { 30 | panic(e) 31 | } 32 | 33 | for _, repo := range repos { 34 | if repo.Name == ".dotfiles" { 35 | println(repo.Name) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /testdata/46-nil-defaults/element/element.go: -------------------------------------------------------------------------------- 1 | package element 2 | 3 | // Element i 4 | type Element interface { 5 | Render() string 6 | Attrs() *attrs 7 | } 8 | 9 | // New fn 10 | func New(tag string, child Element) Element { 11 | return &element{ 12 | tag: tag, 13 | child: child, 14 | } 15 | } 16 | 17 | type element struct { 18 | tag string 19 | child Element 20 | attrs *attrs 21 | } 22 | 23 | type attrs struct { 24 | } 25 | 26 | func (c *element) Render() string { 27 | if c.child == nil { 28 | return "<" + c.tag + ">" 29 | } 30 | return "<" + c.tag + ">" + c.child.Render() + "" 31 | } 32 | 33 | func (c *element) Attrs() *attrs { 34 | return c.attrs 35 | } 36 | -------------------------------------------------------------------------------- /testdata/46-nil-defaults/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/46-nil-defaults/element"] = (function() { 4 | element.prototype.Attrs = function() { 5 | var c = this; 6 | return c.attrs; 7 | }; 8 | function element (o) { 9 | o = o || {}; 10 | this.tag = o.tag || ""; 11 | this.child = o.child || null; 12 | this.attrs = o.attrs || null 13 | }; 14 | function New (tag, child) { 15 | return new element({ 16 | tag: tag, 17 | child: child 18 | }); 19 | }; 20 | element.prototype.Render = function() { 21 | var c = this; 22 | if (c.child == null) { 23 | return "<" + c.tag + ">"; 24 | }; 25 | return "<" + c.tag + ">" + c.child.Render() + ""; 26 | }; 27 | return { 28 | New: New 29 | }; 30 | })(); 31 | pkg["github.com/matthewmueller/joy/testdata/46-nil-defaults"] = (function() { 32 | var element = pkg["github.com/matthewmueller/joy/testdata/46-nil-defaults/element"]; 33 | function main () { 34 | var el = element.New("header", element.New("strong", null)); 35 | console.log(el.Render()); 36 | console.log(el.Attrs()) 37 | }; 38 | return { 39 | main: main 40 | }; 41 | })(); 42 | return pkg["github.com/matthewmueller/joy/testdata/46-nil-defaults"].main(); 43 | })() -------------------------------------------------------------------------------- /testdata/46-nil-defaults/expected.txt: -------------------------------------------------------------------------------- 1 |
2 | null -------------------------------------------------------------------------------- /testdata/46-nil-defaults/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/46-nil-defaults/element" 5 | ) 6 | 7 | // var _ element.Element = (*Page)(nil) 8 | 9 | // func New() element.Element { 10 | 11 | // } 12 | 13 | // type page struct { 14 | // value string 15 | // } 16 | 17 | // func (p *page) Render() string { 18 | // return element.New("header", element.New("strong", nil)).Render() 19 | // } 20 | 21 | func main() { 22 | el := element.New("header", element.New("strong", nil)) 23 | println(el.Render()) 24 | println(el.Attrs()) 25 | } 26 | -------------------------------------------------------------------------------- /testdata/47-circular/element/element.go: -------------------------------------------------------------------------------- 1 | package element 2 | 3 | // Element i 4 | type Element interface { 5 | Render() string 6 | } 7 | 8 | // New fn 9 | func New(tag string, child Element) Element { 10 | return &element{ 11 | tag: tag, 12 | child: child, 13 | } 14 | } 15 | 16 | type element struct { 17 | tag string 18 | child Element 19 | } 20 | 21 | func (c *element) Render() string { 22 | if c.child == nil { 23 | return "<" + c.tag + ">" 24 | } 25 | return "<" + c.tag + ">" + c.child.Render() + "" 26 | } 27 | -------------------------------------------------------------------------------- /testdata/47-circular/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/47-circular/element"] = (function() { 4 | function element (o) { 5 | o = o || {}; 6 | this.tag = o.tag || ""; 7 | this.child = o.child || null 8 | }; 9 | function New (tag, child) { 10 | return new element({ 11 | tag: tag, 12 | child: child 13 | }); 14 | }; 15 | element.prototype.Render = function() { 16 | var c = this; 17 | if (c.child == null) { 18 | return "<" + c.tag + ">"; 19 | }; 20 | return "<" + c.tag + ">" + c.child.Render() + ""; 21 | }; 22 | return { 23 | New: New 24 | }; 25 | })(); 26 | pkg["github.com/matthewmueller/joy/testdata/47-circular"] = (function() { 27 | var element = pkg["github.com/matthewmueller/joy/testdata/47-circular/element"]; 28 | page.prototype.Render = function() { 29 | var p = this; 30 | return element.New("header", element.New("strong", null)).Render(); 31 | }; 32 | function page (o) { 33 | o = o || {} 34 | }; 35 | function main () { 36 | var p = new page({}); 37 | console.log(p.Render.bind(p)()) 38 | }; 39 | return { 40 | main: main 41 | }; 42 | })(); 43 | return pkg["github.com/matthewmueller/joy/testdata/47-circular"].main(); 44 | })() -------------------------------------------------------------------------------- /testdata/47-circular/expected.txt: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /testdata/47-circular/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/47-circular/element" 5 | ) 6 | 7 | type page struct { 8 | } 9 | 10 | func (p *page) Render() string { 11 | return element.New("header", element.New("strong", nil)).Render() 12 | } 13 | 14 | func main() { 15 | p := page{} 16 | println(p.Render()) 17 | } 18 | -------------------------------------------------------------------------------- /testdata/48-func-spread/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/48-func-spread"] = (function() { 4 | function aa (o) { 5 | o = o || {} 6 | }; 7 | function test (a) { 8 | var items = Array.prototype.slice.call(arguments, 1); 9 | return items; 10 | }; 11 | aa.prototype.test2 = function(a) { 12 | var items = Array.prototype.slice.call(arguments, 1); 13 | return items; 14 | }; 15 | function main () { 16 | var items = test.apply(null, ["a", "b", "c"].concat("d")); 17 | for (var _ = 0, item; _ < items.length; _++) { 18 | var item = items[_] 19 | console.log(item) 20 | }; 21 | var a = new aa({}); 22 | items = a.test2.bind(a).apply(null, ["a", "b", "c"].concat("d")); 23 | for (var _ = 0, item; _ < items.length; _++) { 24 | var item = items[_] 25 | console.log(item) 26 | } 27 | }; 28 | return { 29 | main: main 30 | }; 31 | })(); 32 | return pkg["github.com/matthewmueller/joy/testdata/48-func-spread"].main(); 33 | })() -------------------------------------------------------------------------------- /testdata/48-func-spread/expected.txt: -------------------------------------------------------------------------------- 1 | b 2 | c 3 | d 4 | b 5 | c 6 | d -------------------------------------------------------------------------------- /testdata/48-func-spread/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | items := test("a", "b", "c", "d") 5 | for _, item := range items { 6 | println(item) 7 | } 8 | 9 | a := aa{} 10 | items = a.test2("a", "b", "c", "d") 11 | for _, item := range items { 12 | println(item) 13 | } 14 | } 15 | 16 | func test(a string, items ...string) []string { 17 | return items 18 | } 19 | 20 | type aa struct{} 21 | 22 | func (*aa) test2(a string, items ...string) []string { 23 | return items 24 | } 25 | -------------------------------------------------------------------------------- /testdata/50-slice-spreads/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/50-slice-spreads"] = (function() { 4 | function test2 (prefix) { 5 | var results = Array.prototype.slice.call(arguments, 1); 6 | console.log(prefix, results) 7 | }; 8 | function test () { 9 | var results = Array.prototype.slice.call(arguments, 0); 10 | console.log(results); 11 | test2.apply(null, ["hi"].concat(results)); 12 | (function(prefix, results) { 13 | 14 | })("hello", results) 15 | }; 16 | function main () { 17 | test.apply(null, ["a", "b"].concat("c")) 18 | }; 19 | return { 20 | main: main 21 | }; 22 | })(); 23 | return pkg["github.com/matthewmueller/joy/testdata/50-slice-spreads"].main(); 24 | })() -------------------------------------------------------------------------------- /testdata/50-slice-spreads/expected.txt: -------------------------------------------------------------------------------- 1 | [ 'a', 'b', 'c' ] 2 | hi [ 'a', 'b', 'c' ] -------------------------------------------------------------------------------- /testdata/50-slice-spreads/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | test("a", "b", "c") 5 | } 6 | 7 | func test(results ...string) { 8 | println(results) 9 | test2("hi", results...) 10 | 11 | func(prefix string, results ...string) { 12 | // println(prefix, results) 13 | }("hello", results...) 14 | } 15 | 16 | func test2(prefix string, results ...string) { 17 | println(prefix, results) 18 | } 19 | -------------------------------------------------------------------------------- /testdata/51-variadic-rewrite/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/51-variadic-rewrite"] = (function() { 4 | var one = "1"; 5 | function main () { 6 | console.log(1, ["b", "c"].map(function(a) { return one + "a" + a }).join(' ')); 7 | console.log(2, [].map(function(a) { return one + "a" + a }).join(' ')) 8 | }; 9 | return { 10 | main: main 11 | }; 12 | })(); 13 | return pkg["github.com/matthewmueller/joy/testdata/51-variadic-rewrite"].main(); 14 | })() -------------------------------------------------------------------------------- /testdata/51-variadic-rewrite/expected.txt: -------------------------------------------------------------------------------- 1 | 1 1ab 1ac 2 | 2 -------------------------------------------------------------------------------- /testdata/51-variadic-rewrite/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | var one = "1" 6 | 7 | func main() { 8 | println(1, test("a", "b", "c")) 9 | println(2, test("a")) 10 | } 11 | 12 | func test(two string, rest ...string) string { 13 | macro.Rewrite("$2.map(function(a) { return $1 + $3 + a }).join(' ')", one, rest, two) 14 | return "" 15 | } 16 | -------------------------------------------------------------------------------- /testdata/53-rename-variables/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/53-rename-variables"] = (function() { 4 | var body = document.body.nodeName; 5 | function main () { 6 | console.log(body) 7 | }; 8 | return { 9 | body: body, 10 | main: main 11 | }; 12 | })(); 13 | return pkg["github.com/matthewmueller/joy/testdata/53-rename-variables"].main(); 14 | })() -------------------------------------------------------------------------------- /testdata/53-rename-variables/expected.txt: -------------------------------------------------------------------------------- 1 | BODY -------------------------------------------------------------------------------- /testdata/53-rename-variables/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // Body var 6 | // js:"body" 7 | var Body = macro.Raw("document.body.nodeName") 8 | 9 | func main() { 10 | println(Body) 11 | } 12 | -------------------------------------------------------------------------------- /testdata/55-struct-embedding/expected.js: -------------------------------------------------------------------------------- 1 | function inherit(ctor, superCtor) { 2 | ctor.super_ = superCtor 3 | ctor.prototype = Object.create(superCtor.prototype, { 4 | constructor: { 5 | value: ctor, 6 | enumerable: false, 7 | writable: true, 8 | configurable: true 9 | } 10 | }) 11 | } 12 | 13 | function thing() {} 14 | 15 | thing.prototype.kind = function() { 16 | return 1 17 | } 18 | 19 | function animal(o) { 20 | o = o || {} 21 | this.hello = o.hello || '' 22 | for (var k in thing.prototype) { 23 | this[k] = thing.prototype[k] 24 | } 25 | } 26 | 27 | animal.prototype.name = function() { 28 | return 'animal' 29 | } 30 | 31 | function dog(o) { 32 | o = o || {} 33 | this.animal = o.animal || new animal() 34 | 35 | for (var $k in this.animal || animal.prototype) { 36 | this[$k] = (this.animal || animal.prototype)[$k] 37 | } 38 | } 39 | 40 | dog.prototype.legs = function() { 41 | return 4 42 | } 43 | 44 | var d = new dog({ 45 | animal: new animal({ 46 | hello: 'hi' 47 | }) 48 | }) 49 | console.log(d.name(), d.legs(), d.kind(), d.hello) 50 | -------------------------------------------------------------------------------- /testdata/55-struct-embedding/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/55-struct-embedding"] = (function() { 4 | entity.prototype.Entity = function() { 5 | var t = this; 6 | return "ent"; 7 | }; 8 | dog.prototype.Legs = function() { 9 | var d = this; 10 | return 4; 11 | }; 12 | animal.prototype.Name = function() { 13 | var a = this; 14 | return "animal"; 15 | }; 16 | thing.prototype.Type = function() { 17 | var t = this; 18 | return 1; 19 | }; 20 | function entity (o) { 21 | o = o || {} 22 | }; 23 | function thing (o) { 24 | o = o || {} 25 | }; 26 | function animal (o) { 27 | o = o || {}; 28 | this.kind = o.kind || ""; 29 | this.thing = o.thing || null; 30 | for (var $k in this.thing || thing.prototype) { 31 | this[$k] = this[$k] || (this.thing || thing.prototype)[$k] 32 | }; 33 | this.entity = o.entity || new entity(); 34 | for (var $k in this.entity || entity.prototype) { 35 | this[$k] = this[$k] || (this.entity || entity.prototype)[$k] 36 | } 37 | }; 38 | function dog (o) { 39 | o = o || {}; 40 | this.animal = o.animal || null; 41 | for (var $k in this.animal || animal.prototype) { 42 | this[$k] = this[$k] || (this.animal || animal.prototype)[$k] 43 | } 44 | }; 45 | function main () { 46 | var d = new dog({ 47 | animal: new animal({ 48 | kind: "sloth" 49 | }) 50 | }); 51 | console.log(d.Name.bind(d)(), d.Legs.bind(d)(), d.kind, d.Type.bind(d)(), d.Entity.bind(d)()) 52 | }; 53 | return { 54 | main: main 55 | }; 56 | })(); 57 | return pkg["github.com/matthewmueller/joy/testdata/55-struct-embedding"].main(); 58 | })() -------------------------------------------------------------------------------- /testdata/55-struct-embedding/expected.txt: -------------------------------------------------------------------------------- 1 | animal 4 sloth 1 ent -------------------------------------------------------------------------------- /testdata/55-struct-embedding/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type thing struct { 4 | } 5 | 6 | func (t *thing) Type() int { 7 | return 1 8 | } 9 | 10 | type entity struct{} 11 | 12 | func (t *entity) Entity() string { 13 | return "ent" 14 | } 15 | 16 | type animal struct { 17 | kind string 18 | *thing 19 | entity 20 | } 21 | 22 | func (a *animal) Name() string { 23 | return "animal" 24 | } 25 | 26 | type dog struct { 27 | *animal 28 | } 29 | 30 | func (d *dog) Legs() int { 31 | return 4 32 | } 33 | 34 | func main() { 35 | d := dog{ 36 | animal: &animal{ 37 | kind: "sloth", 38 | }, 39 | } 40 | println(d.Name(), d.Legs(), d.kind, d.Type(), d.Entity()) 41 | } 42 | -------------------------------------------------------------------------------- /testdata/57-rewrite-file-dep/expected.txt: -------------------------------------------------------------------------------- 1 | .dotfiles -------------------------------------------------------------------------------- /testdata/57-rewrite-file-dep/fetch/fetch.go: -------------------------------------------------------------------------------- 1 | package fetch 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | ) 6 | 7 | // Options struct 8 | type Options struct { 9 | Method string 10 | } 11 | 12 | // Get fn 13 | func Get(url string) (Response, error) { 14 | macro.Rewrite(`await (async function (unfetch, url) { 15 | try { 16 | var res = await unfetch(url) 17 | return [ res, null ] 18 | } catch (err) { 19 | return [ null, err ] 20 | } 21 | })($1, $2) 22 | `, macro.File("./unfetch.js"), url) 23 | return Response{}, nil 24 | } 25 | 26 | // Response struct 27 | // js:"response,omit" 28 | type Response struct { 29 | } 30 | 31 | // Text gets the textual representation 32 | // js:"text,async" 33 | func (r *Response) Text() []byte { 34 | return nil 35 | } 36 | 37 | // JSON marshals the json 38 | // js:"json,async" 39 | func (r *Response) JSON(v interface{}) error { 40 | macro.Rewrite(`await (async function ($obj) { 41 | try { 42 | var $o = await $_.json() 43 | for (var $k in $o) $obj[$k] = $o[$k] 44 | return null 45 | } catch ($e) { 46 | return $e 47 | } 48 | })($1)`, v) 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /testdata/57-rewrite-file-dep/fetch/unfetch.js: -------------------------------------------------------------------------------- 1 | return function fetch(url, options) { 2 | options = options || {} 3 | return new Promise(function(resolve, reject) { 4 | var request = new XMLHttpRequest() 5 | 6 | request.open(options.method || 'get', url) 7 | 8 | for (var i in options.headers) { 9 | request.setRequestHeader(i, options.headers[i]) 10 | } 11 | 12 | request.withCredentials = options.credentials == 'include' 13 | 14 | request.onload = function() { 15 | resolve(response()) 16 | } 17 | 18 | request.onerror = reject 19 | 20 | request.send(options.body) 21 | 22 | function response() { 23 | var keys = [], 24 | all = [], 25 | headers = {}, 26 | header 27 | 28 | request 29 | .getAllResponseHeaders() 30 | .replace(/^(.*?):\s*([\s\S]*?)$/gm, function(m, key, value) { 31 | keys.push((key = key.toLowerCase())) 32 | all.push([key, value]) 33 | header = headers[key] 34 | headers[key] = header ? header + ',' + value : value 35 | }) 36 | 37 | return { 38 | ok: ((request.status / 200) | 0) == 1, // 200-299 39 | status: request.status, 40 | statusText: request.statusText, 41 | url: request.responseURL, 42 | clone: response, 43 | responseText: request.responseText, 44 | text: function() { 45 | return Promise.resolve(request.responseText) 46 | }, 47 | json: function() { 48 | return Promise.resolve(request.responseText).then(JSON.parse) 49 | }, 50 | blob: function() { 51 | return Promise.resolve(new Blob([request.response])) 52 | }, 53 | headers: { 54 | keys: function() { 55 | return keys 56 | }, 57 | entries: function() { 58 | return all 59 | }, 60 | get: function(n) { 61 | return headers[n.toLowerCase()] 62 | }, 63 | has: function(n) { 64 | return n.toLowerCase() in headers 65 | } 66 | } 67 | } 68 | } 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /testdata/57-rewrite-file-dep/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/57-rewrite-file-dep/fetch" 5 | ) 6 | 7 | // TODO: move tests off external dependencies 8 | // var api = "https://hacker-news.firebaseio.com" 9 | 10 | // Repo struct 11 | type Repo struct { 12 | Name string `js:"name"` 13 | } 14 | 15 | func main() { 16 | response, err := fetch.Get("https://api.github.com/users/matthewmueller/repos") 17 | if err != nil { 18 | println(err) 19 | return 20 | } 21 | 22 | var repos []Repo 23 | if e := response.JSON(&repos); e != nil { 24 | panic(e) 25 | } 26 | 27 | for _, repo := range repos { 28 | if repo.Name == ".dotfiles" { 29 | println(repo.Name) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /testdata/58-time-sleep/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/58-time-sleep"] = (function() { 4 | async function main () { 5 | await new Promise(function(resolve, reject) { setTimeout(resolve, 1 * 1000) }) 6 | }; 7 | return { 8 | main: main 9 | }; 10 | })(); 11 | return pkg["github.com/matthewmueller/joy/testdata/58-time-sleep"].main(); 12 | })() -------------------------------------------------------------------------------- /testdata/58-time-sleep/expected.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewmueller/joy/e60ec6cf98bbbf7935742e6882fa3d33cadc6a78/testdata/58-time-sleep/expected.txt -------------------------------------------------------------------------------- /testdata/58-time-sleep/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func main() { 8 | time.Sleep(1 * time.Second) 9 | } 10 | -------------------------------------------------------------------------------- /testdata/59-rename-method/dep/dep.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | // Test fn 4 | // js:"test" 5 | func Test() []string { 6 | var items []string 7 | return append(items, "a", "b", "c") 8 | } 9 | -------------------------------------------------------------------------------- /testdata/59-rename-method/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/59-rename-method/dep"] = (function() { 4 | function test () { 5 | var items = []; 6 | return items.concat("a", "b", "c"); 7 | }; 8 | return { 9 | test: test 10 | }; 11 | })(); 12 | pkg["github.com/matthewmueller/joy/testdata/59-rename-method"] = (function() { 13 | var dep = pkg["github.com/matthewmueller/joy/testdata/59-rename-method/dep"]; 14 | function main () { 15 | console.log(typeof dep.test); 16 | console.log(dep.test()) 17 | }; 18 | return { 19 | main: main 20 | }; 21 | })(); 22 | return pkg["github.com/matthewmueller/joy/testdata/59-rename-method"].main(); 23 | })() -------------------------------------------------------------------------------- /testdata/59-rename-method/expected.txt: -------------------------------------------------------------------------------- 1 | function 2 | [ 'a', 'b', 'c' ] -------------------------------------------------------------------------------- /testdata/59-rename-method/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | "github.com/matthewmueller/joy/testdata/59-rename-method/dep" 6 | ) 7 | 8 | func main() { 9 | println(macro.Raw("typeof dep.test", dep.Test)) 10 | println(dep.Test()) 11 | } 12 | -------------------------------------------------------------------------------- /testdata/61-method-func-name/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/61-method-func-name"] = (function() { 4 | function test () { 5 | console.log("func") 6 | }; 7 | tester.prototype.test = function() { 8 | var t = this; 9 | console.log("method") 10 | }; 11 | function tester (o) { 12 | o = o || {} 13 | }; 14 | function main () { 15 | test(); 16 | var t = new tester({}); 17 | t.test.bind(t)() 18 | }; 19 | return { 20 | main: main 21 | }; 22 | })(); 23 | return pkg["github.com/matthewmueller/joy/testdata/61-method-func-name"].main(); 24 | })() -------------------------------------------------------------------------------- /testdata/61-method-func-name/expected.txt: -------------------------------------------------------------------------------- 1 | func 2 | method -------------------------------------------------------------------------------- /testdata/61-method-func-name/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | test() 5 | t := tester{} 6 | t.test() 7 | } 8 | 9 | func test() { 10 | println("func") 11 | } 12 | 13 | type tester struct{} 14 | 15 | func (t *tester) test() { 16 | println("method") 17 | } 18 | -------------------------------------------------------------------------------- /testdata/62-rename-interface-methods/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/62-rename-interface-methods"] = (function() { 4 | page.prototype.render = function() { 5 | return "page!"; 6 | }; 7 | function page (o) { 8 | o = o || {} 9 | }; 10 | function newiface () { 11 | return new page({}); 12 | }; 13 | function newpage () { 14 | return new page({}); 15 | }; 16 | function main () { 17 | var p = newpage(); 18 | console.log(p.render.bind(p)()); 19 | var i = newiface(); 20 | console.log(i.render()) 21 | }; 22 | return { 23 | main: main 24 | }; 25 | })(); 26 | return pkg["github.com/matthewmueller/joy/testdata/62-rename-interface-methods"].main(); 27 | })() -------------------------------------------------------------------------------- /testdata/62-rename-interface-methods/expected.txt: -------------------------------------------------------------------------------- 1 | page! 2 | page! -------------------------------------------------------------------------------- /testdata/62-rename-interface-methods/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type component interface { 4 | // js:"render" 5 | Render() string 6 | } 7 | 8 | type page struct { 9 | } 10 | 11 | // js:"render" 12 | func (*page) Render() string { 13 | return "page!" 14 | } 15 | 16 | func main() { 17 | p := newpage() 18 | println(p.Render()) 19 | 20 | i := newiface() 21 | println(i.Render()) 22 | } 23 | 24 | func newpage() *page { 25 | return &page{} 26 | } 27 | 28 | func newiface() component { 29 | return &page{} 30 | } 31 | -------------------------------------------------------------------------------- /testdata/64-other-type-defs/dep/dep.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | // String from other dep 4 | type String string 5 | -------------------------------------------------------------------------------- /testdata/64-other-type-defs/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/64-other-type-defs"] = (function() { 4 | function S (s) { 5 | return s; 6 | }; 7 | function main () { 8 | console.log("hi"); 9 | console.log("hi"); 10 | console.log(5); 11 | console.log(S("hi")); 12 | var m = { 13 | "a": "b" 14 | }; 15 | m["hello"] = "world"; 16 | console.log(m["hello"]); 17 | console.log(m["a"]); 18 | var a = []; 19 | a = a.concat("array"); 20 | console.log(a[0]) 21 | }; 22 | return { 23 | S: S, 24 | main: main 25 | }; 26 | })(); 27 | return pkg["github.com/matthewmueller/joy/testdata/64-other-type-defs"].main(); 28 | })() -------------------------------------------------------------------------------- /testdata/64-other-type-defs/expected.txt: -------------------------------------------------------------------------------- 1 | hi 2 | hi 3 | 5 4 | hi 5 | world 6 | b 7 | array -------------------------------------------------------------------------------- /testdata/64-other-type-defs/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/testdata/64-other-type-defs/dep" 4 | 5 | // String type 6 | type String string 7 | 8 | // Integer type 9 | type Integer int 10 | 11 | // func (s String) concat(s2 string) string { 12 | // return string(s) + s2 13 | // } 14 | 15 | // String2 type 16 | type String2 dep.String 17 | 18 | // Map type 19 | type Map map[string]string 20 | 21 | // Arr type 22 | type Arr []string 23 | 24 | // S fn 25 | func S(s String) String { 26 | return s 27 | } 28 | 29 | func main() { 30 | println(String("hi")) 31 | println(String2("hi")) 32 | println(Integer(5)) 33 | println(S("hi")) 34 | 35 | // println(String("hi").concat("cool")) 36 | 37 | m := Map{"a": "b"} 38 | m["hello"] = "world" 39 | println(m["hello"]) 40 | println(m["a"]) 41 | 42 | var a Arr 43 | a = append(a, "array") 44 | println(a[0]) 45 | } 46 | -------------------------------------------------------------------------------- /testdata/65-dup-method-struct/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/65-dup-method-struct"] = (function() { 4 | function main () { 5 | var w = window; 6 | var doc = document; 7 | console.log(doc.nodeName) 8 | }; 9 | return { 10 | main: main 11 | }; 12 | })(); 13 | return pkg["github.com/matthewmueller/joy/testdata/65-dup-method-struct"].main(); 14 | })() -------------------------------------------------------------------------------- /testdata/65-dup-method-struct/expected.txt: -------------------------------------------------------------------------------- 1 | #document -------------------------------------------------------------------------------- /testdata/65-dup-method-struct/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/macro" 4 | 5 | // Document struct 6 | // js:"document,omit" 7 | type Document struct { 8 | } 9 | 10 | // NodeName fn 11 | // js:"nodeName" 12 | func (d *Document) NodeName() string { 13 | macro.Rewrite("$_.nodeName") 14 | return "" 15 | } 16 | 17 | // New fn 18 | func New() *Window { 19 | macro.Rewrite("window") 20 | return &Window{} 21 | } 22 | 23 | // Window struct 24 | // js:"window,omit" 25 | type Window struct { 26 | } 27 | 28 | // Document fn 29 | func (w *Window) Document() *Document { 30 | macro.Rewrite("document") 31 | return &Document{} 32 | } 33 | 34 | func main() { 35 | w := New() 36 | doc := w.Document() 37 | println(doc.NodeName()) 38 | } 39 | -------------------------------------------------------------------------------- /testdata/66-iface-rewrites/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/66-iface-rewrites"] = (function() { 4 | function window (o) { 5 | o = o || {} 6 | }; 7 | function main () { 8 | var doc = document; 9 | console.log(doc.nodeName); 10 | doc.textContent = "hi" 11 | }; 12 | return { 13 | main: main 14 | }; 15 | })(); 16 | return pkg["github.com/matthewmueller/joy/testdata/66-iface-rewrites"].main(); 17 | })() -------------------------------------------------------------------------------- /testdata/66-iface-rewrites/expected.txt: -------------------------------------------------------------------------------- 1 | #document -------------------------------------------------------------------------------- /testdata/66-iface-rewrites/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/macro" 5 | ) 6 | 7 | // Document interface 8 | type Document interface { 9 | // js:"nodeName" 10 | // jsrewrite:"$_.nodeName" 11 | NodeName() string 12 | // jsrewrite:"$_.textContent = $1" 13 | TextContext(text string) 14 | } 15 | 16 | type document struct { 17 | } 18 | 19 | // js:"nodeName" 20 | func (d *document) NodeName() string { 21 | macro.Rewrite("$_.nodeName") 22 | return "" 23 | } 24 | 25 | func (d *document) TextContext(text string) { 26 | macro.Rewrite("$_.textContent = $1") 27 | } 28 | 29 | type window struct { 30 | } 31 | 32 | // js:"nodeName" 33 | func (d *window) NodeName() string { 34 | macro.Rewrite("$_.nodeName") 35 | return "" 36 | } 37 | 38 | func (d *window) TextContext(text string) { 39 | macro.Rewrite("$_.textContent = $1") 40 | } 41 | 42 | // New fn 43 | func New() (doc Document) { 44 | macro.Rewrite("document") 45 | return &window{} 46 | } 47 | 48 | func main() { 49 | doc := New() 50 | println(doc.NodeName()) 51 | doc.TextContext("hi") 52 | } 53 | -------------------------------------------------------------------------------- /testdata/67-return-values/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/67-return-values"] = (function() { 4 | function test () { 5 | var a = "", b = 0, c = 0, t = null; 6 | return [a, b, c, t]; 7 | }; 8 | function main () { 9 | var $a = test(), a = $a[0], b = $a[1], c = $a[2], t = $a[3]; 10 | console.log(a.length, b, c, t) 11 | }; 12 | return { 13 | main: main 14 | }; 15 | })(); 16 | return pkg["github.com/matthewmueller/joy/testdata/67-return-values"].main(); 17 | })() -------------------------------------------------------------------------------- /testdata/67-return-values/expected.txt: -------------------------------------------------------------------------------- 1 | 0 0 0 null 2 | -------------------------------------------------------------------------------- /testdata/67-return-values/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Test interface{} 4 | 5 | func test() (a string, b, c int, t Test) { 6 | return a, b, c, t 7 | } 8 | 9 | func main() { 10 | a, b, c, t := test() 11 | println(len(a), b, c, t) 12 | } 13 | -------------------------------------------------------------------------------- /testdata/68-methodless-structs/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/68-methodless-structs"] = (function() { 4 | function main () { 5 | var o = { 6 | a: "hi", 7 | b: 0, 8 | c: { 9 | d: 0 10 | } 11 | }; 12 | console.log(o.a, o.b, o.c.d) 13 | }; 14 | return { 15 | main: main 16 | }; 17 | })(); 18 | return pkg["github.com/matthewmueller/joy/testdata/68-methodless-structs"].main(); 19 | })() -------------------------------------------------------------------------------- /testdata/68-methodless-structs/expected.txt: -------------------------------------------------------------------------------- 1 | hi 0 0 -------------------------------------------------------------------------------- /testdata/68-methodless-structs/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Options struct { 4 | a string 5 | b int 6 | c C 7 | } 8 | 9 | type C struct { 10 | d int 11 | } 12 | 13 | func main() { 14 | o := &Options{a: "hi"} 15 | println(o.a, o.b, o.c.d) 16 | } 17 | -------------------------------------------------------------------------------- /testdata/70-stdlib/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/70-stdlib"] = (function() { 4 | function main () { 5 | console.log.apply(console.log, ["hi", "mars"]); 6 | console.log.apply(console.log, ["hi %s"].concat(["mars"])) 7 | }; 8 | return { 9 | main: main 10 | }; 11 | })(); 12 | return pkg["github.com/matthewmueller/joy/testdata/70-stdlib"].main(); 13 | })() -------------------------------------------------------------------------------- /testdata/70-stdlib/expected.txt: -------------------------------------------------------------------------------- 1 | hi mars 2 | hi %s mars -------------------------------------------------------------------------------- /testdata/70-stdlib/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("hi", "mars") 7 | fmt.Printf("hi %s", "mars") 8 | } 9 | -------------------------------------------------------------------------------- /testdata/_41-named-results/dep/dep.go: -------------------------------------------------------------------------------- 1 | package dep 2 | -------------------------------------------------------------------------------- /testdata/_41-named-results/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | } 6 | 7 | // func test(dep.) 8 | -------------------------------------------------------------------------------- /testdata/_47-circular/element/element.go: -------------------------------------------------------------------------------- 1 | package element 2 | 3 | // Element i 4 | type Element interface { 5 | Render() string 6 | } 7 | 8 | // New fn 9 | func New(tag string, child Element) Element { 10 | return &element{ 11 | tag: tag, 12 | child: child, 13 | } 14 | } 15 | 16 | type element struct { 17 | tag string 18 | child Element 19 | } 20 | 21 | func (c *element) Render() string { 22 | if c.child == nil { 23 | return "<" + c.tag + ">" 24 | } 25 | return "<" + c.tag + ">" + c.child.Render() + "" 26 | } 27 | -------------------------------------------------------------------------------- /testdata/_47-circular/expected.js.txt: -------------------------------------------------------------------------------- 1 | ; 2 | (function() { 3 | var pkg = {}; 4 | pkg["github.com/matthewmueller/joy/testdata/47-circular/element"] = (function() { 5 | function New (tag, child) { 6 | return new element({ 7 | tag: tag, 8 | child: child 9 | }); 10 | }; 11 | element.prototype.Render = function() { 12 | var c = this; 13 | if (c.child == null) { 14 | return "<" + c.tag + ">" 15 | }; 16 | return "<" + c.tag + ">" + c.child.Render() + ""; 17 | }; 18 | function element (o) { 19 | o = o || {}; 20 | this.tag = o.tag || ""; 21 | this.child = o.child || null; 22 | }; 23 | return { 24 | New: New 25 | }; 26 | })(); 27 | pkg["github.com/matthewmueller/joy/testdata/47-circular"] = (function() { 28 | var element = pkg["github.com/matthewmueller/joy/testdata/47-circular/element"]; 29 | function main () { 30 | var el = element.New("header", element.New("strong", null)); 31 | console.log(el.Render()); 32 | }; 33 | return { 34 | main: main 35 | }; 36 | })(); 37 | return pkg["github.com/matthewmueller/joy/testdata/47-circular"].main(); 38 | })() -------------------------------------------------------------------------------- /testdata/_47-circular/expected.txt: -------------------------------------------------------------------------------- 1 |
2 | null -------------------------------------------------------------------------------- /testdata/_47-circular/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/testdata/47-circular/element" 5 | ) 6 | 7 | type page struct { 8 | } 9 | 10 | func (p *page) Render() string { 11 | return element.New("header", element.New("strong", nil)).Render() 12 | } 13 | 14 | func main() { 15 | p := page{} 16 | println(p.Render()) 17 | } 18 | -------------------------------------------------------------------------------- /testdata/_49-jsx/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/49-jsx/header"] = (function() { 4 | function Header (o) { 5 | o = o || {}; 6 | this.Component = o.Component || new pkg["github.com/matthewmueller/joy/testdata/49-jsx/preact/preact.js"].Component(); 7 | for (var $k in this.Component || pkg["github.com/matthewmueller/joy/testdata/49-jsx/preact/preact.js"].Component.prototype) { 8 | this[$k] = this[$k] || (this.Component || pkg["github.com/matthewmueller/joy/testdata/49-jsx/preact/preact.js"].Component.prototype)[$k] 9 | }; 10 | this.props = o.props || { 11 | title: "", 12 | bats: "", 13 | children: [] 14 | }; 15 | this.state = o.state || { 16 | count: 0 17 | } 18 | }; 19 | function New (title) { 20 | var children = Array.prototype.slice.call(arguments, 1); 21 | return new Header({ 22 | props: { 23 | title: title, 24 | bats: "", 25 | children: children 26 | }, 27 | state: { 28 | count: 10 29 | } 30 | }); 31 | }; 32 | return { 33 | Header: Header, 34 | New: New 35 | }; 36 | })(); 37 | pkg["runtime"] = (function() { 38 | mapper.prototype.JSON = function() { 39 | var m = this; 40 | return m.obj; 41 | }; 42 | function mapper (o) { 43 | o = o || {}; 44 | this.obj = o.obj || {} 45 | }; 46 | function Map () { 47 | return new mapper({}); 48 | }; 49 | mapper.prototype.Set = function(key, value) { 50 | var m = this; 51 | m.obj[key] = value; 52 | return m; 53 | }; 54 | return { 55 | Map: Map 56 | }; 57 | })(); 58 | pkg["github.com/matthewmueller/joy/testdata/49-jsx"] = (function() { 59 | var header = pkg["github.com/matthewmueller/joy/testdata/49-jsx/header"]; 60 | function main () { 61 | ; 62 | var hdr = pkg["github.com/matthewmueller/joy/testdata/49-jsx/preact/preact.js"].h('h2', pkg["runtime"].Map().Set('class', "hi") ? pkg["runtime"].Map().Set('class', "hi").JSON() : {}, ["yo!", header.New.apply(null, ["lol"].concat("hi!"))]); 63 | var w = window; 64 | var document = w.document; 65 | pkg["github.com/matthewmueller/joy/testdata/49-jsx/preact/preact.js"].render(hdr, document.body); 66 | console.log(document.body.innerHTML) 67 | }; 68 | return { 69 | main: main 70 | }; 71 | })(); 72 | return pkg["github.com/matthewmueller/joy/testdata/49-jsx"].main(); 73 | })() -------------------------------------------------------------------------------- /testdata/_49-jsx/expected.txt: -------------------------------------------------------------------------------- 1 |

yo!

hi!

-------------------------------------------------------------------------------- /testdata/_49-jsx/header/header.go: -------------------------------------------------------------------------------- 1 | package header 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/vdom" 5 | "github.com/matthewmueller/joy/vdom/h3" 6 | ) 7 | 8 | // Header struct 9 | type Header struct { 10 | vdom.Component 11 | 12 | props props 13 | state state 14 | } 15 | 16 | // Props for the header 17 | type props struct { 18 | title string 19 | bats string 20 | children []vdom.Child 21 | } 22 | 23 | type state struct { 24 | count int 25 | } 26 | 27 | // - type-safe props + state 28 | // - no generation step so it has IDE proper support 29 | // - go-compatible for SSR 30 | // - not too much boilerplate to create components 31 | // - works with other types of like html / text nodes 32 | // - works in all enumerations of code 33 | 34 | // New header 35 | func New(title string, children ...vdom.Child) vdom.Component { 36 | return &Header{ 37 | props: props{ 38 | title: title, 39 | children: children, 40 | }, 41 | state: state{ 42 | count: 10, 43 | }, 44 | } 45 | } 46 | 47 | // ComponentWillMount fn 48 | // js:"componentWillMount,keep" 49 | func (d *Header) ComponentWillMount() { 50 | d.state.count = 5 51 | } 52 | 53 | func (d *Header) onClick(e interface{}) { 54 | d.SetState(&state{ 55 | count: d.state.count + 1, 56 | }) 57 | } 58 | 59 | // Render header 60 | // js:"render" 61 | func (d *Header) Render() vdom.Node { 62 | return h3.New(h3.Class(d.props.title).Attr("count", d.state.count), d.props.children...) 63 | } 64 | -------------------------------------------------------------------------------- /testdata/_49-jsx/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /testdata/_49-jsx/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/htmlbodyelement" 5 | "github.com/matthewmueller/joy/dom/window" 6 | "github.com/matthewmueller/joy/testdata/49-jsx/header" 7 | "github.com/matthewmueller/joy/testdata/49-jsx/preact" 8 | "github.com/matthewmueller/joy/vdom" 9 | "github.com/matthewmueller/joy/vdom/h2" 10 | ) 11 | 12 | func main() { 13 | vdom.Use("preact.h", "./preact/preact.js") 14 | 15 | hdr := h2.New(h2.Class("hi"), 16 | vdom.S("yo!"), 17 | header.New("lol", vdom.S("hi!")), 18 | ) 19 | 20 | w := window.New() 21 | document := w.Document() 22 | preact.Render(hdr, document.Body().(*htmlbodyelement.HTMLBodyElement)) 23 | println(document.Body().InnerHTML()) 24 | } 25 | -------------------------------------------------------------------------------- /testdata/_49-jsx/preact/preact.go: -------------------------------------------------------------------------------- 1 | package preact 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/window" 5 | "github.com/matthewmueller/joy/macro" 6 | "github.com/matthewmueller/joy/vdom" 7 | ) 8 | 9 | // Render the component 10 | func Render(component vdom.Child, el window.Node) { 11 | macro.Rewrite("$1.render($2, $3)", vdom.File(), component, el) 12 | } 13 | -------------------------------------------------------------------------------- /testdata/_52-basic-dom/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/52-basic-dom"] = (function() { 4 | function main () { 5 | var document = window.document; 6 | var a = document.createElement("a"); 7 | console.log(a.nodeName); 8 | var strong = document.createElement("strong"); 9 | console.log(document.createElement("strong").outerHTML); 10 | a.appendChild(strong); 11 | strong.textContent = "hi world!"; 12 | var body = document.body; 13 | body.appendChild(a); 14 | console.log(document.body.outerHTML) 15 | }; 16 | return { 17 | main: main 18 | }; 19 | })(); 20 | return pkg["github.com/matthewmueller/joy/testdata/52-basic-dom"].main(); 21 | })() -------------------------------------------------------------------------------- /testdata/_52-basic-dom/expected.txt: -------------------------------------------------------------------------------- 1 | A 2 | 3 | hi world! -------------------------------------------------------------------------------- /testdata/_52-basic-dom/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/htmlbodyelement" 5 | "github.com/matthewmueller/joy/dom/window" 6 | ) 7 | 8 | func main() { 9 | document := window.New().Document() 10 | 11 | a := document.CreateElement("a") 12 | println(a.NodeName()) 13 | strong := document.CreateElement("strong") 14 | println(document.CreateElement("strong").OuterHTML()) 15 | a.AppendChild(strong) 16 | 17 | strong.SetTextContent("hi world!") 18 | 19 | body := document.Body().(*htmlbodyelement.HTMLBodyElement) 20 | body.AppendChild(a) 21 | println(document.Body().OuterHTML()) 22 | } 23 | -------------------------------------------------------------------------------- /testdata/_54-basic-jolly/expected.txt: -------------------------------------------------------------------------------- 1 |

yo!

hi!0

-------------------------------------------------------------------------------- /testdata/_54-basic-jolly/header/header.go: -------------------------------------------------------------------------------- 1 | package header 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/matthewmueller/joy/macro" 7 | "github.com/matthewmueller/joy/vdom" 8 | "github.com/matthewmueller/joy/vdom/h/h3" 9 | ) 10 | 11 | // Header struct 12 | type Header struct { 13 | vdom.Component 14 | 15 | // props 16 | props props 17 | 18 | // state 19 | state state 20 | } 21 | 22 | type props struct { 23 | title string 24 | children []vdom.Child 25 | bats string 26 | } 27 | 28 | // State struct 29 | type state struct { 30 | count int 31 | } 32 | 33 | // New Header 34 | func New(title string, children ...vdom.Child) vdom.Component { 35 | return &Header{ 36 | props: props{ 37 | title: title, 38 | children: children, 39 | bats: "are crazy", 40 | }, 41 | } 42 | } 43 | 44 | // MouseEvent struct 45 | // js:"mouseevent,omit" 46 | type MouseEvent struct { 47 | Type string `js:"type"` 48 | } 49 | 50 | // OnClick fn 51 | func (d *Header) OnClick(e interface{}) { 52 | println(macro.Raw("e.type")) 53 | d.SetState(&state{ 54 | count: d.state.count + 1, 55 | }) 56 | } 57 | 58 | // Render header 59 | // js:"render" 60 | func (d *Header) Render() vdom.Node { 61 | children := append(d.props.children, vdom.S(strconv.Itoa(d.state.count))) 62 | return h3.New(h3.Class(d.props.title).Attr("count", d.state.count), children...) 63 | } 64 | -------------------------------------------------------------------------------- /testdata/_54-basic-jolly/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /testdata/_54-basic-jolly/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/document" 5 | "github.com/matthewmueller/joy/testdata/54-basic-jolly/header" 6 | "github.com/matthewmueller/joy/testdata/54-basic-jolly/preact" 7 | "github.com/matthewmueller/joy/vdom" 8 | "github.com/matthewmueller/joy/vdom/h/h2" 9 | ) 10 | 11 | func main() { 12 | vdom.Use("preact.h", "./preact/preact.js") 13 | 14 | hdr := h2.New(h2.Class("hi"), 15 | vdom.S("yo!"), 16 | header.New("lol", vdom.S("hi!")), 17 | ) 18 | 19 | preact.Render(hdr, document.Body) 20 | println(document.Body.InnerHTML()) 21 | } 22 | -------------------------------------------------------------------------------- /testdata/_54-basic-jolly/preact/preact.go: -------------------------------------------------------------------------------- 1 | package preact 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/document" 5 | "github.com/matthewmueller/joy/macro" 6 | "github.com/matthewmueller/joy/vdom" 7 | ) 8 | 9 | // var preact = macro.File("./preact.js") 10 | 11 | // var _ jsx.Node = (*Component)(nil) 12 | 13 | // Component interface 14 | // js:"component,omit" 15 | // type Component struct{} 16 | 17 | // // Render fn 18 | // func (c *Component) Render() jsx.JSX { 19 | // return nil 20 | // } 21 | 22 | // // SetState fn 23 | // // js:"setState" 24 | // func (c *Component) SetState(interface{}) { 25 | 26 | // } 27 | 28 | // // ForceUpdate fn 29 | // func (c *Component) ForceUpdate() { 30 | 31 | // } 32 | 33 | // Render the component 34 | func Render(component vdom.Child, el *document.Node) { 35 | macro.Rewrite("$1.render($2, $3)", vdom.File(), component, el) 36 | } 37 | -------------------------------------------------------------------------------- /testdata/_60-chaining/expected.txt: -------------------------------------------------------------------------------- 1 | did mount 2 | did mount 3 | some textsubbody 4 | -------------------------------------------------------------------------------- /testdata/_60-chaining/header/header.go: -------------------------------------------------------------------------------- 1 | package header 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/matthewmueller/joy/vdom" 7 | "github.com/matthewmueller/joy/vdom/h/strong" 8 | ) 9 | 10 | // import "github.com/matthewmueller/joy/macro" 11 | 12 | // header struct 13 | type header struct { 14 | vdom.Component 15 | 16 | props *props 17 | state *state 18 | } 19 | 20 | type props struct { 21 | title string 22 | body string 23 | children []vdom.Child 24 | } 25 | 26 | type state struct { 27 | id string 28 | } 29 | 30 | // New fn 31 | func New(title string, body string, children ...vdom.Child) vdom.Component { 32 | return &header{ 33 | props: &props{ 34 | title: title, 35 | body: body, 36 | children: children, 37 | }, 38 | } 39 | } 40 | 41 | // Render function 42 | func (h *header) Render() vdom.Node { 43 | return strong.New(strong.Class(h.props.title).ID(h.state.id), h.props.children...) 44 | } 45 | 46 | // ComponentWillMount function 47 | func (h *header) ComponentWillMount() { 48 | h.state.id = strconv.Itoa(5) 49 | } 50 | 51 | // ComponentDidMount function 52 | func (h *header) ComponentDidMount() { 53 | println("did mount") 54 | } 55 | -------------------------------------------------------------------------------- /testdata/_60-chaining/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/document" 5 | "github.com/matthewmueller/joy/testdata/60-chaining/header" 6 | "github.com/matthewmueller/joy/testdata/60-chaining/preact" 7 | "github.com/matthewmueller/joy/vdom" 8 | "github.com/matthewmueller/joy/vdom/h/strong" 9 | ) 10 | 11 | // - type-safe props + state 12 | // - no generation step so it has IDE proper support 13 | // - go-compatible for SSR 14 | // - not too much boilerplate to create components 15 | // - works with other types of like html / text nodes 16 | // - works in all enumerations of code 17 | // - works with other virtual dom libraries (particularly preact) 18 | // - can have multiple elements in a single file 19 | 20 | func main() { 21 | vdom.Use("preact.h", "./preact/preact.js") 22 | 23 | v := header.New("some title", "some body", 24 | strong.New(strong.Class("some class").ID("some id"), 25 | vdom.S("some text"), 26 | header.New("subtitle", "subbody", vdom.S("subbody")), 27 | ), 28 | ) 29 | 30 | preact.Render(v, document.Body) 31 | println(document.Body.InnerHTML()) 32 | } 33 | -------------------------------------------------------------------------------- /testdata/_60-chaining/preact/preact.go: -------------------------------------------------------------------------------- 1 | package preact 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/document" 5 | "github.com/matthewmueller/joy/macro" 6 | "github.com/matthewmueller/joy/vdom" 7 | ) 8 | 9 | var _ vdom.Component = (*Component)(nil) 10 | 11 | // Component interface 12 | // js:"component,omit" 13 | type Component struct{} 14 | 15 | // Render fn 16 | func (c *Component) Render() vdom.Node { 17 | return nil 18 | } 19 | 20 | // SetState fn 21 | // js:"setState" 22 | func (c *Component) SetState(interface{}) { 23 | 24 | } 25 | 26 | // ForceUpdate fn 27 | func (c *Component) ForceUpdate() { 28 | 29 | } 30 | 31 | // Render the component 32 | func Render(component vdom.Component, el *document.Node) { 33 | macro.Rewrite("$1.render($2, $3)", macro.File("./preact.js"), component, el) 34 | } 35 | 36 | // String turns the component into a string 37 | func String(component vdom.Component) string { 38 | return component.Render().String() 39 | } 40 | -------------------------------------------------------------------------------- /testdata/_63-vdom-zero-values/expected.txt: -------------------------------------------------------------------------------- 1 | matt 28 555-5555 2 | matt 28 555-5555 -------------------------------------------------------------------------------- /testdata/_63-vdom-zero-values/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/matthewmueller/joy/dom/document" 8 | "github.com/matthewmueller/joy/vdom" 9 | "github.com/matthewmueller/joy/vdom/h/strong" 10 | ) 11 | 12 | // User struct 13 | type User struct { 14 | vdom.Component 15 | 16 | props *props 17 | } 18 | 19 | type props struct { 20 | FirstName string 21 | LastName string 22 | Age int 23 | Phone Phone 24 | } 25 | 26 | // Phone struct 27 | type Phone struct { 28 | Type string 29 | Number string 30 | } 31 | 32 | // Render fn 33 | func (u *User) Render() vdom.Node { 34 | if u.props.Age == 0 { 35 | u.props.Age = 28 36 | } 37 | 38 | if u.props.FirstName == "" { 39 | u.props.FirstName = "matt" 40 | } 41 | 42 | if u.props.Phone.Number == "" { 43 | u.props.Phone.Number = "555-5555" 44 | } 45 | 46 | props := []string{u.props.FirstName, strconv.Itoa(u.props.Age), u.props.Phone.Number, u.props.Phone.Type} 47 | return strong.New(nil, vdom.S(strings.Join(props, " "))) 48 | } 49 | 50 | func main() { 51 | vdom.Use("preact.h", "./preact.js") 52 | user := &User{props: &props{}} 53 | vdom.Render(user, document.Body, nil) 54 | println(document.Body.InnerHTML()) 55 | user = &User{} 56 | vdom.Render(user, document.Body, document.Body.FirstElementChild()) 57 | println(document.Body.InnerHTML()) 58 | } 59 | -------------------------------------------------------------------------------- /testdata/_69-dom-iface-rewrites/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/69-dom-iface-rewrites"] = (function() { 4 | function main () { 5 | var w = window; 6 | var doc = w.document; 7 | var html = doc.documentElement; 8 | console.log(html.tagName) 9 | }; 10 | return { 11 | main: main 12 | }; 13 | })(); 14 | return pkg["github.com/matthewmueller/joy/testdata/69-dom-iface-rewrites"].main(); 15 | })() -------------------------------------------------------------------------------- /testdata/_69-dom-iface-rewrites/expected.txt: -------------------------------------------------------------------------------- 1 | HTML -------------------------------------------------------------------------------- /testdata/_69-dom-iface-rewrites/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/matthewmueller/joy/dom/window" 4 | 5 | func main() { 6 | w := window.New() 7 | doc := w.Document() 8 | html := doc.DocumentElement() 9 | println(html.TagName()) 10 | } 11 | -------------------------------------------------------------------------------- /testdata/_71-vdom/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["runtime"] = (function() { 4 | mapper.prototype.JSON = function() { 5 | var m = this; 6 | return m.obj; 7 | }; 8 | function mapper (o) { 9 | o = o || {}; 10 | this.obj = o.obj || {} 11 | }; 12 | function Map () { 13 | return new mapper({}); 14 | }; 15 | mapper.prototype.Set = function(key, value) { 16 | var m = this; 17 | m.obj[key] = value; 18 | return m; 19 | }; 20 | return { 21 | Map: Map 22 | }; 23 | })(); 24 | pkg["github.com/matthewmueller/joy/testdata/71-vdom"] = (function() { 25 | function main () { 26 | ; 27 | var hdr = pkg["github.com/matthewmueller/joy/testdata/71-vdom/preact.js"].h('html', pkg["runtime"].Map().Set('manifest', "hi").Set('class', "cool") ? pkg["runtime"].Map().Set('manifest', "hi").Set('class', "cool").JSON() : {}, [pkg["github.com/matthewmueller/joy/testdata/71-vdom/preact.js"].h('head', null ? null.JSON() : {}, []), pkg["github.com/matthewmueller/joy/testdata/71-vdom/preact.js"].h('body', null ? null.JSON() : {}, ["hi world"])]); 28 | var w = window; 29 | var document = w.document; 30 | File().render(hdr, document.body, null); 31 | console.log(document.body.innerHTML) 32 | }; 33 | return { 34 | main: main 35 | }; 36 | })(); 37 | return pkg["github.com/matthewmueller/joy/testdata/71-vdom"].main(); 38 | })() -------------------------------------------------------------------------------- /testdata/_71-vdom/expected.txt: -------------------------------------------------------------------------------- 1 | hi world -------------------------------------------------------------------------------- /testdata/_71-vdom/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/matthewmueller/joy/dom/htmlbodyelement" 5 | "github.com/matthewmueller/joy/dom/window" 6 | "github.com/matthewmueller/joy/vdom" 7 | "github.com/matthewmueller/joy/vdom/body" 8 | "github.com/matthewmueller/joy/vdom/head" 9 | "github.com/matthewmueller/joy/vdom/html" 10 | ) 11 | 12 | func main() { 13 | vdom.Use("preact.h", "./preact.js") 14 | 15 | hdr := html.New(html.Manifest("hi").Class("cool"), 16 | head.New(nil), 17 | body.New(nil, vdom.S("hi world")), 18 | ) 19 | 20 | w := window.New() 21 | document := w.Document() 22 | vdom.Render(hdr, document.Body().(*htmlbodyelement.HTMLBodyElement), nil) 23 | println(document.Body().InnerHTML()) 24 | } 25 | -------------------------------------------------------------------------------- /testdata/_72-defer/expected.js.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewmueller/joy/e60ec6cf98bbbf7935742e6882fa3d33cadc6a78/testdata/_72-defer/expected.js.txt -------------------------------------------------------------------------------- /testdata/_72-defer/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | func main() { 6 | test() 7 | } 8 | 9 | func test() { 10 | defer func() { 11 | println("deferred") 12 | }() 13 | time.Sleep(100 * time.Millisecond) 14 | println("slept") 15 | } 16 | -------------------------------------------------------------------------------- /testdata/_73-init/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/73-init"] = (function() { 4 | function main () { 5 | console.log("main") 6 | }; 7 | return { 8 | main: main 9 | }; 10 | })(); 11 | return pkg["github.com/matthewmueller/joy/testdata/73-init"].main(); 12 | })() -------------------------------------------------------------------------------- /testdata/_73-init/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func init() { 4 | println("init") 5 | } 6 | 7 | func main() { 8 | println("main") 9 | } 10 | -------------------------------------------------------------------------------- /testdata/_74-switch/expected.js.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewmueller/joy/e60ec6cf98bbbf7935742e6882fa3d33cadc6a78/testdata/_74-switch/expected.js.txt -------------------------------------------------------------------------------- /testdata/_74-switch/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | switch "a" { 5 | case "a": 6 | println("a") 7 | default: 8 | println("default") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /testdata/_75-select/expected.js.txt: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var pkg = {}; 3 | pkg["github.com/matthewmueller/joy/testdata/75-switch"] = (function() { 4 | function main () { 5 | 6 | }; 7 | return { 8 | main: main 9 | }; 10 | })(); 11 | return pkg["github.com/matthewmueller/joy/testdata/75-switch"].main(); 12 | })() -------------------------------------------------------------------------------- /testdata/_75-select/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan string, 1) 5 | select { 6 | case str := <-ch: 7 | println(str) 8 | default: 9 | println("default") 10 | } 11 | } 12 | --------------------------------------------------------------------------------