init
This commit is contained in:
parent
d807b8cfad
commit
a541c14e0e
4 changed files with 302 additions and 0 deletions
30
d2js/README.md
Normal file
30
d2js/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# D2 as a Javascript library
|
||||
|
||||
D2 is runnable as a Javascript library, on both the client and server side. This means you
|
||||
can run D2 entirely on the browser.
|
||||
|
||||
This is achieved by a JS wrapper around a WASM file.
|
||||
|
||||
## Install
|
||||
|
||||
### NPM
|
||||
|
||||
```sh
|
||||
npm install @terrastruct/d2
|
||||
```
|
||||
|
||||
### Yarn
|
||||
|
||||
```sh
|
||||
yarn add @terrastruct/d2
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
GOOS=js GOARCH=wasm go build -ldflags='-s -w' -trimpath -o main.wasm ./d2js
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
todo
|
||||
259
d2js/js.go
Normal file
259
d2js/js.go
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
//go:build wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"strings"
|
||||
"syscall/js"
|
||||
|
||||
"oss.terrastruct.com/d2/d2ast"
|
||||
"oss.terrastruct.com/d2/d2compiler"
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
"oss.terrastruct.com/d2/d2oracle"
|
||||
"oss.terrastruct.com/d2/d2parser"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/lib/urlenc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
js.Global().Set("d2GetObjOrder", js.FuncOf(jsGetObjOrder))
|
||||
js.Global().Set("d2GetRefRanges", js.FuncOf(jsGetRefRanges))
|
||||
js.Global().Set("d2Compile", js.FuncOf(jsCompile))
|
||||
js.Global().Set("d2Parse", js.FuncOf(jsParse))
|
||||
js.Global().Set("d2Encode", js.FuncOf(jsEncode))
|
||||
js.Global().Set("d2Decode", js.FuncOf(jsDecode))
|
||||
select {}
|
||||
}
|
||||
|
||||
type jsObjOrder struct {
|
||||
Order []string `json:"order"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func jsGetObjOrder(this js.Value, args []js.Value) interface{} {
|
||||
dsl := args[0].String()
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
|
||||
UTF16: true,
|
||||
})
|
||||
if err != nil {
|
||||
ret := jsObjOrder{Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
resp := jsObjOrder{
|
||||
Order: d2oracle.GetObjOrder(g),
|
||||
}
|
||||
|
||||
str, _ := json.Marshal(resp)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
type jsRefRanges struct {
|
||||
Ranges []d2ast.Range `json:"ranges"`
|
||||
ParseError string `json:"parseError"`
|
||||
UserError string `json:"userError"`
|
||||
D2Error string `json:"d2Error"`
|
||||
}
|
||||
|
||||
func jsGetRefRanges(this js.Value, args []js.Value) interface{} {
|
||||
dsl := args[0].String()
|
||||
key := args[1].String()
|
||||
|
||||
mk, err := d2parser.ParseMapKey(key)
|
||||
if err != nil {
|
||||
ret := jsRefRanges{D2Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
|
||||
UTF16: true,
|
||||
})
|
||||
var pe *d2parser.ParseError
|
||||
if err != nil {
|
||||
if errors.As(err, &pe) {
|
||||
serialized, _ := json.Marshal(err)
|
||||
// TODO
|
||||
ret := jsRefRanges{ParseError: string(serialized)}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
ret := jsRefRanges{D2Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
var ranges []d2ast.Range
|
||||
if len(mk.Edges) == 1 {
|
||||
edge := d2oracle.GetEdge(g, key)
|
||||
if edge == nil {
|
||||
ret := jsRefRanges{D2Error: "edge not found"}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
for _, ref := range edge.References {
|
||||
ranges = append(ranges, ref.MapKey.Range)
|
||||
}
|
||||
} else {
|
||||
obj := d2oracle.GetObj(g, key)
|
||||
if obj == nil {
|
||||
ret := jsRefRanges{D2Error: "obj not found"}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
for _, ref := range obj.References {
|
||||
ranges = append(ranges, ref.Key.Range)
|
||||
}
|
||||
}
|
||||
|
||||
resp := jsRefRanges{
|
||||
Ranges: ranges,
|
||||
}
|
||||
|
||||
str, _ := json.Marshal(resp)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
type jsObject struct {
|
||||
Result string `json:"result"`
|
||||
UserError string `json:"userError"`
|
||||
D2Error string `json:"d2Error"`
|
||||
}
|
||||
|
||||
type jsParseResponse struct {
|
||||
DSL string `json:"dsl"`
|
||||
Texts []*d2target.MText `json:"texts"`
|
||||
ParseError string `json:"parseError"`
|
||||
UserError string `json:"userError"`
|
||||
D2Error string `json:"d2Error"`
|
||||
}
|
||||
|
||||
type blockFS struct{}
|
||||
|
||||
func (blockFS blockFS) Open(name string) (fs.File, error) {
|
||||
return nil, errors.New("import statements not currently implemented")
|
||||
}
|
||||
|
||||
func jsParse(this js.Value, args []js.Value) interface{} {
|
||||
dsl := args[0].String()
|
||||
themeID := args[1].Int()
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
|
||||
UTF16: true,
|
||||
FS: blockFS{},
|
||||
})
|
||||
var pe *d2parser.ParseError
|
||||
if err != nil {
|
||||
if errors.As(err, &pe) {
|
||||
serialized, _ := json.Marshal(err)
|
||||
ret := jsParseResponse{ParseError: string(serialized)}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
ret := jsParseResponse{D2Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
if len(g.Layers) > 0 || len(g.Scenarios) > 0 || len(g.Steps) > 0 {
|
||||
ret := jsParseResponse{UserError: "layers, scenarios, and steps are not yet supported. Coming soon."}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
for _, o := range g.Objects {
|
||||
if (o.Attributes.Top == nil) != (o.Attributes.Left == nil) {
|
||||
ret := jsParseResponse{UserError: `keywords "top" and "left" currently must be used together`}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
}
|
||||
|
||||
err = g.ApplyTheme(int64(themeID))
|
||||
if err != nil {
|
||||
ret := jsParseResponse{D2Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
resp := jsParseResponse{
|
||||
Texts: g.Texts(),
|
||||
}
|
||||
|
||||
newDSL := d2format.Format(g.AST)
|
||||
if dsl != newDSL {
|
||||
resp.DSL = newDSL
|
||||
}
|
||||
|
||||
str, _ := json.Marshal(resp)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
// TODO error passing
|
||||
// TODO recover panics
|
||||
func jsCompile(this js.Value, args []js.Value) interface{} {
|
||||
script := args[0].String()
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(script), &d2compiler.CompileOptions{
|
||||
UTF16: true,
|
||||
})
|
||||
var pe *d2parser.ParseError
|
||||
if err != nil {
|
||||
if errors.As(err, &pe) {
|
||||
serialized, _ := json.Marshal(err)
|
||||
ret := jsObject{UserError: string(serialized)}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
ret := jsObject{D2Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
newScript := d2format.Format(g.AST)
|
||||
if script != newScript {
|
||||
ret := jsObject{Result: newScript}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func jsEncode(this js.Value, args []js.Value) interface{} {
|
||||
script := args[0].String()
|
||||
|
||||
encoded, err := urlenc.Encode(script)
|
||||
// should never happen
|
||||
if err != nil {
|
||||
ret := jsObject{D2Error: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
ret := jsObject{Result: encoded}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
func jsDecode(this js.Value, args []js.Value) interface{} {
|
||||
script := args[0].String()
|
||||
|
||||
script, err := urlenc.Decode(script)
|
||||
if err != nil {
|
||||
ret := jsObject{UserError: err.Error()}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
ret := jsObject{Result: script}
|
||||
str, _ := json.Marshal(ret)
|
||||
return string(str)
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
//go:build !wasm
|
||||
|
||||
package d2latex
|
||||
|
||||
import (
|
||||
|
|
|
|||
11
d2renderers/d2latex/latex_stub.go
Normal file
11
d2renderers/d2latex/latex_stub.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//go:build wasm
|
||||
|
||||
package d2latex
|
||||
|
||||
func Render(s string) (_ string, err error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func Measure(s string) (width, height int, err error) {
|
||||
return
|
||||
}
|
||||
Loading…
Reference in a new issue