diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 9e7cabe91..7f7f63458 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -19,6 +19,7 @@ - Support for `--omit-version` flag. [#2377](https://github.com/terrastruct/d2/issues/2377) - Compiler: - `link`s can be set to root path, e.g. `/xyz`. [#2357](https://github.com/terrastruct/d2/issues/2357) + - When importing a file, attempt resolving substitutions at the imported file scope first [#2482](https://github.com/terrastruct/d2/pull/2482) - Parser: - impose max key length. It's almost certainly a mistake if an ID gets too long, e.g. missing quotes [#2465](https://github.com/terrastruct/d2/pull/2465) - Render: diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index da5e686ba..44c4d1bb2 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -1714,6 +1714,29 @@ steps: { assert.Equal(t, 1, len(g.Layers[0].Steps)) }, }, + { + name: "import-nested-var", + + text: `...@models.environment +`, + files: map[string]string{ + "models.d2": ` +vars: { + c: { + k + } +} + +environment: { + ...${c} +} +`, + }, + assertions: func(t *testing.T, g *d2graph.Graph) { + assert.Equal(t, 1, len(g.Objects)) + assert.Equal(t, "k", g.Objects[0].AbsID()) + }, + }, { name: "import-connections", diff --git a/d2ir/import.go b/d2ir/import.go index 6f593c5c7..2d6630203 100644 --- a/d2ir/import.go +++ b/d2ir/import.go @@ -111,6 +111,14 @@ func (c *compiler) __import(imp *d2ast.Import) (*Map, bool) { c.compileMap(ir, ast, ast) + // We attempt to resolve variables in the imported file scope first + // But ignore errors, in case the variable is meant to be resolved at the + // importer + savedErrors := make([]d2ast.Error, len(c.err.Errors)) + copy(savedErrors, c.err.Errors) + c.compileSubstitutions(ir, nil) + c.err.Errors = savedErrors + c.seenImports[impPath] = struct{}{} return ir, true diff --git a/testdata/d2compiler/TestCompile/import-nested-var.exp.json b/testdata/d2compiler/TestCompile/import-nested-var.exp.json new file mode 100644 index 000000000..a2ec401d7 --- /dev/null +++ b/testdata/d2compiler/TestCompile/import-nested-var.exp.json @@ -0,0 +1,116 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/import-nested-var.d2,0:0:0-1:0:23", + "nodes": [ + { + "import": { + "range": "d2/testdata/d2compiler/TestCompile/import-nested-var.d2,0:0:0-0:22:22", + "spread": true, + "pre": "", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/import-nested-var.d2,0:4:4-0:10:10", + "value": [ + { + "string": "models", + "raw_string": "models" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/import-nested-var.d2,0:11:11-0:22:22", + "value": [ + { + "string": "environment", + "raw_string": "environment" + } + ] + } + } + ] + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "iconStyle": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "k", + "id_val": "k", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/models.d2,3:4:20-3:5:21", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/models.d2,3:4:20-3:5:21", + "value": [ + { + "string": "k", + "raw_string": "k" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "k" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "iconStyle": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +}