diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 5480ca65c..2f4bdaecd 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -18,3 +18,4 @@ - Render: Multi-line class labels/headers are rendered correctly [#2057](https://github.com/terrastruct/d2/pull/2057) - CLI: Watch mode uses correct backlinks (`_` usages) [#2058](https://github.com/terrastruct/d2/pull/2058) - Vars: Spread variables are inserted in place instead of appending to end of scope [#2062](https://github.com/terrastruct/d2/pull/2062) +- Imports: fix local icon imports from files that are imported [#2066](https://github.com/terrastruct/d2/pull/2066) diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go index d11f9735f..f82da677d 100644 --- a/d2ast/d2ast.go +++ b/d2ast/d2ast.go @@ -1431,3 +1431,7 @@ func (i *Import) PathWithPre() string { } return path.Join(i.Pre, i.Path[0].Unbox().ScalarString()) } + +func (i *Import) Dir() string { + return path.Dir(i.PathWithPre()) +} diff --git a/d2ir/compile.go b/d2ir/compile.go index 20af53d62..9da3a7a4e 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -1,7 +1,10 @@ package d2ir import ( + "html" "io/fs" + "net/url" + "path" "strconv" "strings" @@ -558,7 +561,8 @@ func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) { } OverlayMap(dst, impn.Map()) - c.extendLinks(dst, ParentField(dst)) + impDir := n.Import.Dir() + c.extendLinks(dst, ParentField(dst), impDir) if impnf, ok := impn.(*Field); ok { if impnf.Primary_ != nil { @@ -862,7 +866,8 @@ func (c *compiler) _compileField(f *Field, refctx *RefContext) { } } OverlayMap(f.Map(), n) - c.extendLinks(f.Map(), f) + impDir := refctx.Key.Value.Import.Dir() + c.extendLinks(f.Map(), f, impDir) switch NodeBoardKind(f) { case BoardScenario, BoardStep: c.overlayClasses(f.Map()) @@ -895,9 +900,10 @@ func (c *compiler) ignoreLazyGlob(n Node) bool { return false } -// When importing a file, all of its board links need to be extended to reflect their new path -func (c *compiler) extendLinks(m *Map, importF *Field) { +// When importing a file, all of its board and icon links need to be extended to reflect their new path +func (c *compiler) extendLinks(m *Map, importF *Field, importDir string) { nodeBoardKind := NodeBoardKind(m) + importIDA := IDA(importF) for _, f := range m.Fields { if f.Name == "link" { if nodeBoardKind != "" { @@ -914,14 +920,23 @@ func (c *compiler) extendLinks(m *Map, importF *Field) { continue } - importIDA := IDA(importF) extendedIDA := append(importIDA, linkIDA[1:]...) kp := d2ast.MakeKeyPath(extendedIDA) s := d2format.Format(kp) f.Primary_.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(s)).ScalarBox().Unbox() } + if f.Name == "icon" { + val := f.Primary().Value.ScalarString() + u, err := url.Parse(html.UnescapeString(val)) + isRemoteImg := err == nil && strings.HasPrefix(u.Scheme, "http") + if isRemoteImg { + continue + } + val = path.Join(importDir, val) + f.Primary_.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(val)).ScalarBox().Unbox() + } if f.Map() != nil { - c.extendLinks(f.Map(), importF) + c.extendLinks(f.Map(), importF, importDir) } } } diff --git a/e2etests-cli/main_test.go b/e2etests-cli/main_test.go index 6182232f7..d051530fa 100644 --- a/e2etests-cli/main_test.go +++ b/e2etests-cli/main_test.go @@ -711,6 +711,19 @@ steps: { assert.Testdata(t, ".svg", svg) }, }, + { + name: "import_icon_relative", + run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) { + writeFile(t, dir, "hello-world.d2", `...@asdf/x`) + writeFile(t, filepath.Join(dir, "asdf"), "x.d2", `y: { icon: ./blah.svg }; z: { icon: ../root.svg }`) + writeFile(t, filepath.Join(dir, "asdf"), "blah.svg", ``) + writeFile(t, dir, "root.svg", ``) + err := runTestMain(t, ctx, dir, env, filepath.Join(dir, "hello-world.d2")) + assert.Success(t, err) + svg := readFile(t, dir, "hello-world.svg") + assert.Testdata(t, ".svg", svg) + }, + }, { name: "chain_import", run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) { diff --git a/e2etests-cli/testdata/TestCLI_E2E/import_icon_relative.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/import_icon_relative.exp.svg new file mode 100644 index 000000000..5a3562a0f --- /dev/null +++ b/e2etests-cli/testdata/TestCLI_E2E/import_icon_relative.exp.svg @@ -0,0 +1,96 @@ +yz + + + +