diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 577b13d0e..e42b8be2a 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -28,6 +28,8 @@ - d2cli: - Support `validate` command. [#2415](https://github.com/terrastruct/d2/pull/2415) +- d2compiler: + - `link`s can be set to root path, e.g. `/xyz`. [#2357](https://github.com/terrastruct/d2/issues/2357) #### Bugfixes ⛑️ diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 97f55f4a1..34e49c383 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -1265,7 +1265,7 @@ func (c *compiler) validateBoardLinks(g *d2graph.Graph) { } u, err := url.Parse(html.UnescapeString(obj.Link.Value)) - isRemote := err == nil && u.Scheme != "" + isRemote := err == nil && (u.Scheme != "" || strings.HasPrefix(u.Path, "/")) if isRemote { continue } diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 47183fe9c..c64c18919 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -1658,6 +1658,22 @@ x -> y: { } }, }, + { + name: "url_relative_link", + + text: `x: { + link: /google +} +`, + assertions: func(t *testing.T, g *d2graph.Graph) { + if len(g.Objects) != 1 { + t.Fatal(g.Objects) + } + if g.Objects[0].Link.Value != "/google" { + t.Fatal(g.Objects[0].Link.Value) + } + }, + }, { name: "non_url_link", diff --git a/d2ir/compile.go b/d2ir/compile.go index a239862a8..82f3559d1 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -1146,7 +1146,7 @@ func (c *compiler) extendLinks(m *Map, importF *Field, importDir string) { val := f.Primary().Value.ScalarString() u, err := url.Parse(html.UnescapeString(val)) - isRemote := err == nil && u.Scheme != "" + isRemote := err == nil && (u.Scheme != "" || strings.HasPrefix(u.Path, "/")) if isRemote { continue } @@ -1184,7 +1184,7 @@ func (c *compiler) extendLinks(m *Map, importF *Field, importDir string) { continue } u, err := url.Parse(html.UnescapeString(val)) - isRemoteImg := err == nil && u.Scheme != "" + isRemoteImg := err == nil && (u.Scheme != "" || strings.HasPrefix(u.Path, "/")) if isRemoteImg { continue } diff --git a/testdata/d2compiler/TestCompile/url_relative_link.exp.json b/testdata/d2compiler/TestCompile/url_relative_link.exp.json new file mode 100644 index 000000000..a781e613f --- /dev/null +++ b/testdata/d2compiler/TestCompile/url_relative_link.exp.json @@ -0,0 +1,148 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:0:0-3:0:23", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:0:0-2:1:22", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:3:3-2:1:22", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,1:2:7-1:15:20", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,1:2:7-1:6:11", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,1:2:7-1:6:11", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,1:8:13-1:15:20", + "value": [ + { + "string": "/google", + "raw_string": "/google" + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "x", + "id_val": "x", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_relative_link.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "x" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "/google" + }, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +}