# SafeJS  [![Go Reference](https://pkg.go.dev/badge/github.com/hack-pad/safejs.svg)](https://pkg.go.dev/github.com/hack-pad/safejs) [![CI](https://github.com/hack-pad/safejs/actions/workflows/ci.yml/badge.svg)](https://github.com/hack-pad/safejs/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/hack-pad/safejs/badge.svg?branch=main)](https://coveralls.io/github/hack-pad/safejs?branch=main)

A safer, drop-in replacement for Go's `syscall/js` JavaScript package.

## What makes it safer?

Today, `syscall/js` panics when the JavaScript runtime throws errors.
While sensible in a JavaScript runtime, [Go libraries should avoid using `panic`](https://go.dev/doc/effective_go#panic).

SafeJS provides a nearly identical API to `syscall/js`, but returns errors instead of panicking.

Although returned errors aren't pretty, they make it much easier to integrate with existing Go tools and code patterns.

#### Backward compatibility

This package uses the same backward compatibility guarantee as `syscall/js`.

In an effort to align with the Go standard library API, some breaking changes may become necessary and receive their own minor version bumps.

## Quick start

1. Import `safejs`:
```go
import "github.com/hack-pad/safejs"
```
2. Replace uses of `syscall/js` with the `safejs` alternative. 

Before:
```go
//go:build js && wasm

package buttons

import "syscall/js"

// InsertButton creates a new button, adds it to 'container', and returns it. Usually.
func InsertButton(container js.Value) js.Value {
    // *whisper:* There's a good chance it could panic! Eh, probably don't need to document it, right?
    dom := js.Global().Get("document") // BOOM!
    button := dom.Call("createElement", "button") // BANG!
    container.Call("appendChild", button) // BAM!
    return button
}
```

After:
```go
//go:build js && wasm

package buttons

import "github.com/hack-pad/safejs"

// InsertButton creates a new button, adds it to 'container', and returns the button or the first error.
func InsertButton(container safejs.Value) (safejs.Value, error) {
    dom, err := safejs.Global().Get("document")
    if err != nil {
        return err
    }
    button, err := dom.Call("createElement", "button")
    if err != nil {
        return err
    }
    _, err = container.Call("appendChild", button)
    if err != nil {
        return err
    }
    return button, nil
}
```

## Even safer

For additional JavaScript safety, use the `jsguard` linter too.

`jsguard` reports the locations of unsafe JavaScript calls, which should be replaced with calls to SafeJS.

```bash
go install github.com/hack-pad/safejs/jsguard/cmd/jsguard
export GOOS=js GOARCH=wasm
jsguard ./...
```

It *does not* report use of types like `js.Value` -- only function calls on those types.

This makes it easy to integrate SafeJS into existing libraries which expose only standard library types.
