Skip to content

Commit 7f15c4a

Browse files
committed
init
1 parent 90297e1 commit 7f15c4a

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed

d2js/README.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# D2 as a Javascript library
2+
3+
D2 is runnable as a Javascript library, on both the client and server side. This means you
4+
can run D2 entirely on the browser.
5+
6+
This is achieved by a JS wrapper around a WASM file.
7+
8+
## Install
9+
10+
### NPM
11+
12+
```sh
13+
npm install @terrastruct/d2
14+
```
15+
16+
### Yarn
17+
18+
```sh
19+
yarn add @terrastruct/d2
20+
```
21+
22+
## Build
23+
24+
```sh
25+
GOOS=js GOARCH=wasm go build -ldflags='-s -w' -trimpath -o main.wasm ./d2js
26+
```
27+
28+
## API
29+
30+
todo

d2js/js.go

+259
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
//go:build wasm
2+
3+
package main
4+
5+
import (
6+
"encoding/json"
7+
"errors"
8+
"io/fs"
9+
"strings"
10+
"syscall/js"
11+
12+
"oss.terrastruct.com/d2/d2ast"
13+
"oss.terrastruct.com/d2/d2compiler"
14+
"oss.terrastruct.com/d2/d2format"
15+
"oss.terrastruct.com/d2/d2oracle"
16+
"oss.terrastruct.com/d2/d2parser"
17+
"oss.terrastruct.com/d2/d2target"
18+
"oss.terrastruct.com/d2/lib/urlenc"
19+
)
20+
21+
func main() {
22+
js.Global().Set("d2GetObjOrder", js.FuncOf(jsGetObjOrder))
23+
js.Global().Set("d2GetRefRanges", js.FuncOf(jsGetRefRanges))
24+
js.Global().Set("d2Compile", js.FuncOf(jsCompile))
25+
js.Global().Set("d2Parse", js.FuncOf(jsParse))
26+
js.Global().Set("d2Encode", js.FuncOf(jsEncode))
27+
js.Global().Set("d2Decode", js.FuncOf(jsDecode))
28+
select {}
29+
}
30+
31+
type jsObjOrder struct {
32+
Order []string `json:"order"`
33+
Error string `json:"error"`
34+
}
35+
36+
func jsGetObjOrder(this js.Value, args []js.Value) interface{} {
37+
dsl := args[0].String()
38+
39+
g, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
40+
UTF16: true,
41+
})
42+
if err != nil {
43+
ret := jsObjOrder{Error: err.Error()}
44+
str, _ := json.Marshal(ret)
45+
return string(str)
46+
}
47+
48+
resp := jsObjOrder{
49+
Order: d2oracle.GetObjOrder(g),
50+
}
51+
52+
str, _ := json.Marshal(resp)
53+
return string(str)
54+
}
55+
56+
type jsRefRanges struct {
57+
Ranges []d2ast.Range `json:"ranges"`
58+
ParseError string `json:"parseError"`
59+
UserError string `json:"userError"`
60+
D2Error string `json:"d2Error"`
61+
}
62+
63+
func jsGetRefRanges(this js.Value, args []js.Value) interface{} {
64+
dsl := args[0].String()
65+
key := args[1].String()
66+
67+
mk, err := d2parser.ParseMapKey(key)
68+
if err != nil {
69+
ret := jsRefRanges{D2Error: err.Error()}
70+
str, _ := json.Marshal(ret)
71+
return string(str)
72+
}
73+
74+
g, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
75+
UTF16: true,
76+
})
77+
var pe *d2parser.ParseError
78+
if err != nil {
79+
if errors.As(err, &pe) {
80+
serialized, _ := json.Marshal(err)
81+
// TODO
82+
ret := jsRefRanges{ParseError: string(serialized)}
83+
str, _ := json.Marshal(ret)
84+
return string(str)
85+
}
86+
ret := jsRefRanges{D2Error: err.Error()}
87+
str, _ := json.Marshal(ret)
88+
return string(str)
89+
}
90+
91+
var ranges []d2ast.Range
92+
if len(mk.Edges) == 1 {
93+
edge := d2oracle.GetEdge(g, key)
94+
if edge == nil {
95+
ret := jsRefRanges{D2Error: "edge not found"}
96+
str, _ := json.Marshal(ret)
97+
return string(str)
98+
}
99+
100+
for _, ref := range edge.References {
101+
ranges = append(ranges, ref.MapKey.Range)
102+
}
103+
} else {
104+
obj := d2oracle.GetObj(g, key)
105+
if obj == nil {
106+
ret := jsRefRanges{D2Error: "obj not found"}
107+
str, _ := json.Marshal(ret)
108+
return string(str)
109+
}
110+
111+
for _, ref := range obj.References {
112+
ranges = append(ranges, ref.Key.Range)
113+
}
114+
}
115+
116+
resp := jsRefRanges{
117+
Ranges: ranges,
118+
}
119+
120+
str, _ := json.Marshal(resp)
121+
return string(str)
122+
}
123+
124+
type jsObject struct {
125+
Result string `json:"result"`
126+
UserError string `json:"userError"`
127+
D2Error string `json:"d2Error"`
128+
}
129+
130+
type jsParseResponse struct {
131+
DSL string `json:"dsl"`
132+
Texts []*d2target.MText `json:"texts"`
133+
ParseError string `json:"parseError"`
134+
UserError string `json:"userError"`
135+
D2Error string `json:"d2Error"`
136+
}
137+
138+
type blockFS struct{}
139+
140+
func (blockFS blockFS) Open(name string) (fs.File, error) {
141+
return nil, errors.New("import statements not currently implemented")
142+
}
143+
144+
func jsParse(this js.Value, args []js.Value) interface{} {
145+
dsl := args[0].String()
146+
themeID := args[1].Int()
147+
148+
g, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
149+
UTF16: true,
150+
FS: blockFS{},
151+
})
152+
var pe *d2parser.ParseError
153+
if err != nil {
154+
if errors.As(err, &pe) {
155+
serialized, _ := json.Marshal(err)
156+
ret := jsParseResponse{ParseError: string(serialized)}
157+
str, _ := json.Marshal(ret)
158+
return string(str)
159+
}
160+
ret := jsParseResponse{D2Error: err.Error()}
161+
str, _ := json.Marshal(ret)
162+
return string(str)
163+
}
164+
165+
if len(g.Layers) > 0 || len(g.Scenarios) > 0 || len(g.Steps) > 0 {
166+
ret := jsParseResponse{UserError: "layers, scenarios, and steps are not yet supported. Coming soon."}
167+
str, _ := json.Marshal(ret)
168+
return string(str)
169+
}
170+
171+
for _, o := range g.Objects {
172+
if (o.Attributes.Top == nil) != (o.Attributes.Left == nil) {
173+
ret := jsParseResponse{UserError: `keywords "top" and "left" currently must be used together`}
174+
str, _ := json.Marshal(ret)
175+
return string(str)
176+
}
177+
}
178+
179+
err = g.ApplyTheme(int64(themeID))
180+
if err != nil {
181+
ret := jsParseResponse{D2Error: err.Error()}
182+
str, _ := json.Marshal(ret)
183+
return string(str)
184+
}
185+
186+
resp := jsParseResponse{
187+
Texts: g.Texts(),
188+
}
189+
190+
newDSL := d2format.Format(g.AST)
191+
if dsl != newDSL {
192+
resp.DSL = newDSL
193+
}
194+
195+
str, _ := json.Marshal(resp)
196+
return string(str)
197+
}
198+
199+
// TODO error passing
200+
// TODO recover panics
201+
func jsCompile(this js.Value, args []js.Value) interface{} {
202+
script := args[0].String()
203+
204+
g, err := d2compiler.Compile("", strings.NewReader(script), &d2compiler.CompileOptions{
205+
UTF16: true,
206+
})
207+
var pe *d2parser.ParseError
208+
if err != nil {
209+
if errors.As(err, &pe) {
210+
serialized, _ := json.Marshal(err)
211+
ret := jsObject{UserError: string(serialized)}
212+
str, _ := json.Marshal(ret)
213+
return string(str)
214+
}
215+
ret := jsObject{D2Error: err.Error()}
216+
str, _ := json.Marshal(ret)
217+
return string(str)
218+
}
219+
220+
newScript := d2format.Format(g.AST)
221+
if script != newScript {
222+
ret := jsObject{Result: newScript}
223+
str, _ := json.Marshal(ret)
224+
return string(str)
225+
}
226+
227+
return nil
228+
}
229+
230+
func jsEncode(this js.Value, args []js.Value) interface{} {
231+
script := args[0].String()
232+
233+
encoded, err := urlenc.Encode(script)
234+
// should never happen
235+
if err != nil {
236+
ret := jsObject{D2Error: err.Error()}
237+
str, _ := json.Marshal(ret)
238+
return string(str)
239+
}
240+
241+
ret := jsObject{Result: encoded}
242+
str, _ := json.Marshal(ret)
243+
return string(str)
244+
}
245+
246+
func jsDecode(this js.Value, args []js.Value) interface{} {
247+
script := args[0].String()
248+
249+
script, err := urlenc.Decode(script)
250+
if err != nil {
251+
ret := jsObject{UserError: err.Error()}
252+
str, _ := json.Marshal(ret)
253+
return string(str)
254+
}
255+
256+
ret := jsObject{Result: script}
257+
str, _ := json.Marshal(ret)
258+
return string(str)
259+
}

d2renderers/d2latex/latex.go

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

35
import (

d2renderers/d2latex/latex_stub.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build wasm
2+
3+
package d2latex
4+
5+
func Render(s string) (_ string, err error) {
6+
return "", nil
7+
}
8+
9+
func Measure(s string) (width, height int, err error) {
10+
return
11+
}

0 commit comments

Comments
 (0)