Skip to content

Commit 3dee7bb

Browse files
committed
compile and render functions
1 parent 9e20ed8 commit 3dee7bb

File tree

10 files changed

+147
-50
lines changed

10 files changed

+147
-50
lines changed

d2js/d2wasm/api.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package d2wasm
55
import (
66
"encoding/json"
77
"fmt"
8+
"runtime/debug"
89
"syscall/js"
910
)
1011

@@ -36,7 +37,7 @@ func wrapWASMCall(fn func(args []js.Value) (interface{}, error)) js.Func {
3637
if r := recover(); r != nil {
3738
resp := WASMResponse{
3839
Error: &WASMError{
39-
Message: fmt.Sprintf("panic recovered: %v", r),
40+
Message: fmt.Sprintf("panic recovered: %v\n%s", r, debug.Stack()),
4041
Code: 500,
4142
},
4243
}

d2js/d2wasm/functions.go

+111-14
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,30 @@
33
package d2wasm
44

55
import (
6+
"context"
67
"encoding/json"
8+
"fmt"
79
"strings"
810
"syscall/js"
911

1012
"oss.terrastruct.com/d2/d2ast"
1113
"oss.terrastruct.com/d2/d2compiler"
1214
"oss.terrastruct.com/d2/d2format"
15+
"oss.terrastruct.com/d2/d2graph"
16+
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
17+
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
18+
"oss.terrastruct.com/d2/d2lib"
1319
"oss.terrastruct.com/d2/d2lsp"
1420
"oss.terrastruct.com/d2/d2oracle"
1521
"oss.terrastruct.com/d2/d2parser"
22+
"oss.terrastruct.com/d2/d2renderers/d2fonts"
23+
"oss.terrastruct.com/d2/d2renderers/d2svg"
24+
"oss.terrastruct.com/d2/lib/log"
25+
"oss.terrastruct.com/d2/lib/memfs"
26+
"oss.terrastruct.com/d2/lib/textmeasure"
27+
"oss.terrastruct.com/d2/lib/urlenc"
1628
"oss.terrastruct.com/d2/lib/version"
29+
"oss.terrastruct.com/util-go/go2"
1730
)
1831

1932
func GetParentID(args []js.Value) (interface{}, error) {
@@ -96,26 +109,104 @@ func GetRefRanges(args []js.Value) (interface{}, error) {
96109

97110
func Compile(args []js.Value) (interface{}, error) {
98111
if len(args) < 1 {
99-
return nil, &WASMError{Message: "missing script argument", Code: 400}
112+
return nil, &WASMError{Message: "missing JSON argument", Code: 400}
113+
}
114+
var input CompileRequest
115+
if err := json.Unmarshal([]byte(args[0].String()), &input); err != nil {
116+
return nil, &WASMError{Message: "invalid JSON input", Code: 400}
100117
}
101118

102-
script := args[0].String()
103-
g, _, err := d2compiler.Compile("", strings.NewReader(script), &d2compiler.CompileOptions{
104-
UTF16Pos: true,
105-
})
119+
if input.FS == nil {
120+
return nil, &WASMError{Message: "missing 'fs' field in input JSON", Code: 400}
121+
}
122+
123+
if _, ok := input.FS["index"]; !ok {
124+
return nil, &WASMError{Message: "missing 'index' file in input fs", Code: 400}
125+
}
126+
127+
fs, err := memfs.New(input.FS)
128+
if err != nil {
129+
return nil, &WASMError{Message: fmt.Sprintf("invalid fs input: %s", err.Error()), Code: 400}
130+
}
131+
132+
ruler, err := textmeasure.NewRuler()
133+
if err != nil {
134+
return nil, &WASMError{Message: fmt.Sprintf("text ruler cannot be initialized: %s", err.Error()), Code: 500}
135+
}
136+
ctx := log.WithDefault(context.Background())
137+
layoutFunc := d2dagrelayout.DefaultLayout
138+
if input.Opts != nil && input.Opts.Layout != nil {
139+
switch *input.Opts.Layout {
140+
case "dagre":
141+
layoutFunc = d2dagrelayout.DefaultLayout
142+
case "elk":
143+
layoutFunc = d2elklayout.DefaultLayout
144+
default:
145+
return nil, &WASMError{Message: fmt.Sprintf("layout option '%s' not recognized", *input.Opts.Layout), Code: 400}
146+
}
147+
}
148+
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
149+
return layoutFunc, nil
150+
}
151+
152+
renderOpts := &d2svg.RenderOpts{}
153+
var fontFamily *d2fonts.FontFamily
154+
if input.Opts != nil && input.Opts.Sketch != nil {
155+
fontFamily = go2.Pointer(d2fonts.HandDrawn)
156+
renderOpts.Sketch = input.Opts.Sketch
157+
}
158+
if input.Opts != nil && input.Opts.ThemeID != nil {
159+
renderOpts.ThemeID = input.Opts.ThemeID
160+
}
161+
diagram, g, err := d2lib.Compile(ctx, input.FS["index"], &d2lib.CompileOptions{
162+
UTF16Pos: true,
163+
FS: fs,
164+
Ruler: ruler,
165+
LayoutResolver: layoutResolver,
166+
FontFamily: fontFamily,
167+
}, renderOpts)
106168
if err != nil {
107169
if pe, ok := err.(*d2parser.ParseError); ok {
108170
return nil, &WASMError{Message: pe.Error(), Code: 400}
109171
}
110172
return nil, &WASMError{Message: err.Error(), Code: 500}
111173
}
112174

113-
newScript := d2format.Format(g.AST)
114-
if script != newScript {
115-
return map[string]string{"result": newScript}, nil
175+
input.FS["index"] = d2format.Format(g.AST)
176+
177+
return CompileResponse{
178+
FS: input.FS,
179+
Diagram: *diagram,
180+
Graph: *g,
181+
}, nil
182+
}
183+
184+
func Render(args []js.Value) (interface{}, error) {
185+
if len(args) < 1 {
186+
return nil, &WASMError{Message: "missing JSON argument", Code: 400}
187+
}
188+
var input RenderRequest
189+
if err := json.Unmarshal([]byte(args[0].String()), &input); err != nil {
190+
return nil, &WASMError{Message: "invalid JSON input", Code: 400}
191+
}
192+
193+
if input.Diagram == nil {
194+
return nil, &WASMError{Message: "missing 'diagram' field in input JSON", Code: 400}
195+
}
196+
197+
renderOpts := &d2svg.RenderOpts{}
198+
if input.Opts != nil && input.Opts.Sketch != nil {
199+
renderOpts.Sketch = input.Opts.Sketch
200+
}
201+
if input.Opts != nil && input.Opts.ThemeID != nil {
202+
renderOpts.ThemeID = input.Opts.ThemeID
203+
}
204+
out, err := d2svg.Render(input.Diagram, renderOpts)
205+
if err != nil {
206+
return nil, &WASMError{Message: fmt.Sprintf("render failed: %s", err.Error()), Code: 500}
116207
}
117208

118-
return nil, nil
209+
return out, nil
119210
}
120211

121212
func GetBoardAtPosition(args []js.Value) (interface{}, error) {
@@ -144,7 +235,13 @@ func Encode(args []js.Value) (interface{}, error) {
144235
}
145236

146237
script := args[0].String()
147-
return map[string]string{"result": script}, nil
238+
encoded, err := urlenc.Encode(script)
239+
// should never happen
240+
if err != nil {
241+
return nil, &WASMError{Message: err.Error(), Code: 500}
242+
}
243+
244+
return map[string]string{"result": encoded}, nil
148245
}
149246

150247
func Decode(args []js.Value) (interface{}, error) {
@@ -153,6 +250,10 @@ func Decode(args []js.Value) (interface{}, error) {
153250
}
154251

155252
script := args[0].String()
253+
script, err := urlenc.Decode(script)
254+
if err != nil {
255+
return nil, &WASMError{Message: err.Error(), Code: 500}
256+
}
156257
return map[string]string{"result": script}, nil
157258
}
158259

@@ -189,7 +290,3 @@ func GetCompletions(args []js.Value) (interface{}, error) {
189290
Items: items,
190291
}, nil
191292
}
192-
193-
type CompletionResponse struct {
194-
Items []map[string]interface{} `json:"items"`
195-
}

d2js/d2wasm/types.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
package d2wasm
44

5-
import "oss.terrastruct.com/d2/d2ast"
5+
import (
6+
"oss.terrastruct.com/d2/d2ast"
7+
"oss.terrastruct.com/d2/d2graph"
8+
"oss.terrastruct.com/d2/d2target"
9+
)
610

711
type WASMResponse struct {
812
Data interface{} `json:"data,omitempty"`
@@ -26,3 +30,29 @@ type RefRangesResponse struct {
2630
type BoardPositionResponse struct {
2731
BoardPath []string `json:"boardPath"`
2832
}
33+
34+
type CompileRequest struct {
35+
FS map[string]string `json:"fs"`
36+
Opts *RenderOptions `json:"options"`
37+
}
38+
39+
type RenderOptions struct {
40+
Layout *string `json:"layout"`
41+
Sketch *bool `json:"sketch"`
42+
ThemeID *int64 `json:"themeID"`
43+
}
44+
45+
type CompileResponse struct {
46+
FS map[string]string `json:"fs"`
47+
Diagram d2target.Diagram `json:"diagram"`
48+
Graph d2graph.Graph `json:"graph"`
49+
}
50+
51+
type CompletionResponse struct {
52+
Items []map[string]interface{} `json:"items"`
53+
}
54+
55+
type RenderRequest struct {
56+
Diagram *d2target.Diagram `json:"diagram"`
57+
Opts *RenderOptions `json:"options"`
58+
}

d2js/js.go

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func main() {
1616
api.Register("getObjOrder", d2wasm.GetObjOrder)
1717
api.Register("getRefRanges", d2wasm.GetRefRanges)
1818
api.Register("compile", d2wasm.Compile)
19+
api.Register("render", d2wasm.Render)
1920
api.Register("getBoardAtPosition", d2wasm.GetBoardAtPosition)
2021
api.Register("encode", d2wasm.Encode)
2122
api.Register("decode", d2wasm.Decode)

d2renderers/d2latex/latex.go

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build !wasm
2-
31
package d2latex
42

53
import (

d2renderers/d2latex/latex_stub.go

-11
This file was deleted.

d2renderers/d2svg/d2svg.go

+2
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,8 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
18671867
}
18681868
darkThemeID = opts.DarkThemeID
18691869
scale = opts.Scale
1870+
} else {
1871+
opts = &RenderOpts{}
18701872
}
18711873

18721874
buf := &bytes.Buffer{}

lib/textmeasure/markdown.go

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build !wasm
2-
31
package textmeasure
42

53
import (

lib/textmeasure/markdown_js.go

-17
This file was deleted.

lib/textmeasure/substitutions.go

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build !wasm
2-
31
package textmeasure
42

53
import (

0 commit comments

Comments
 (0)