diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 6ac5a6024..f6a6c2ddd 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -1463,6 +1463,13 @@ func compileConfig(ir *d2ir.Map) (*d2target.Config, error) { } config.DarkThemeOverrides = overrides } + f = configMap.GetField("data") + if f != nil && f.Map() != nil { + config.Data = make(map[string]string) + for _, f := range f.Map().Fields { + config.Data[f.Name] = f.Primary().Value.ScalarString() + } + } return config, nil } diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 40f132bdb..8895749ff 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -4573,6 +4573,22 @@ x: { `, `d2/testdata/d2compiler/TestCompile2/vars/config/not-root.d2:4:4: "d2-config" can only appear at root vars`) }, }, + { + name: "data", + run: func(t *testing.T) { + _, config := assertCompile(t, ` +vars: { + d2-config: { + data: { + cat: hat + } + } +} +`, ``) + assert.Equal(t, 1, len(config.Data)) + assert.Equal(t, "hat", config.Data["cat"]) + }, + }, } for _, tc := range tca { diff --git a/d2ir/compile.go b/d2ir/compile.go index 1e61d1d0f..55f5aeee8 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -178,7 +178,7 @@ func (c *compiler) validateConfigs(configs *Field) { for _, f := range configs.Map().Fields { var val string if f.Primary() == nil { - if f.Name != "theme-overrides" && f.Name != "dark-theme-overrides" { + if f.Name != "theme-overrides" && f.Name != "dark-theme-overrides" && f.Name != "data" { c.errorf(f.LastRef().AST(), `"%s" needs a value`, f.Name) continue } @@ -193,7 +193,7 @@ func (c *compiler) validateConfigs(configs *Field) { c.errorf(f.LastRef().AST(), `expected a boolean for "%s", got "%s"`, f.Name, val) continue } - case "theme-overrides", "dark-theme-overrides": + case "theme-overrides", "dark-theme-overrides", "data": if f.Map() == nil { c.errorf(f.LastRef().AST(), `"%s" needs a map`, f.Name) continue diff --git a/d2target/d2target.go b/d2target/d2target.go index d7825d8bc..29445d803 100644 --- a/d2target/d2target.go +++ b/d2target/d2target.go @@ -47,6 +47,9 @@ type Config struct { LayoutEngine *string `json:"layoutEngine"` ThemeOverrides *ThemeOverrides `json:"themeOverrides,omitempty"` DarkThemeOverrides *ThemeOverrides `json:"darkThemeOverrides,omitempty"` + // Data is a data structure for holding user-defined data + // useful for plugins that allow users to configure within source code + Data map[string]string `json:"data,omitempty"` } type ThemeOverrides struct { diff --git a/testdata/d2compiler/TestCompile2/vars/config/data.exp.json b/testdata/d2compiler/TestCompile2/vars/config/data.exp.json new file mode 100644 index 000000000..daacbb092 --- /dev/null +++ b/testdata/d2compiler/TestCompile2/vars/config/data.exp.json @@ -0,0 +1,157 @@ +{ + "graph": { + "name": "", + "isFolderOnly": true, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,0:0:0-8:0:60", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,1:0:1-7:1:59", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,1:0:1-1:4:5", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,1:0:1-1:4:5", + "value": [ + { + "string": "vars", + "raw_string": "vars" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,1:6:7-7:1:59", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,2:1:10-6:3:57", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,2:1:10-2:10:19", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,2:1:10-2:10:19", + "value": [ + { + "string": "d2-config", + "raw_string": "d2-config" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,2:12:21-6:3:57", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,3:2:25-5:5:53", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,3:2:25-3:6:29", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,3:2:25-3:6:29", + "value": [ + { + "string": "data", + "raw_string": "data" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,3:8:31-5:5:53", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,4:6:39-4:14:47", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,4:6:39-4:9:42", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,4:6:39-4:9:42", + "value": [ + { + "string": "cat", + "raw_string": "cat" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/config/data.d2,4:11:44-4:14:47", + "value": [ + { + "string": "hat", + "raw_string": "hat" + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "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": null + }, + "err": null +}