diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index a29f10f45..48661ab23 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -24,6 +24,9 @@ - Support relative imports. Improve elk error handling: [#2382](https://github.com/terrastruct/d2/pull/2382) - Support fonts (`fontRegular`, `fontItalic`, `fontBold`, `fontSemiBold`): [#2384](https://github.com/terrastruct/d2/pull/2384) +- d2cli: + - Support `validate` command. [#2415](https://github.com/terrastruct/d2/pull/2415) + #### Bugfixes ⛑️ - Compiler: diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index 24ebdd7e1..70269172b 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -17,6 +17,8 @@ .Ar fmt Ar file.d2 ... .Nm d2 .Ar play Ar file.d2 +.Nm d2 +.Ar validate Ar file.d2 .Sh DESCRIPTION .Nm compiles and renders @@ -162,6 +164,8 @@ Lists available themes Format all passed files .It Ar play Ar file.d2 Opens the file in playground, an online web viewer (https://play.d2lang.com) +.It Ar validate Ar file.d2 +Validates file.d2 .El .Sh ENVIRONMENT VARIABLES Many flags can also be set with environment variables. diff --git a/d2cli/help.go b/d2cli/help.go index eb80c85bf..5b025b6bb 100644 --- a/d2cli/help.go +++ b/d2cli/help.go @@ -23,6 +23,7 @@ Usage: %[1]s layout [name] %[1]s fmt file.d2 ... %[1]s play [--theme=0] [--sketch] file.d2 + %[1]s validate file.d2 %[1]s compiles and renders file.d2 to file.svg | file.png It defaults to file.svg if an output path is not provided. @@ -40,6 +41,7 @@ Subcommands: %[1]s themes - Lists available themes %[1]s fmt file.d2 ... - Format passed files %[1]s play file.d2 - Opens the file in playground, an online web viewer (https://play.d2lang.com) + %[1]s validate file.d2 - Validates file.d2 See more docs and the source code at https://oss.terrastruct.com/d2. Hosted icons at https://icons.terrastruct.com. diff --git a/d2cli/main.go b/d2cli/main.go index dbf1ae2c4..3eb486d70 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -173,6 +173,8 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { return fmtCmd(ctx, ms, *checkFlag) case "play": return playCmd(ctx, ms) + case "validate": + return validateCmd(ctx, ms) case "version": if len(ms.Opts.Flags.Args()) > 1 { return xmain.UsageErrorf("version subcommand accepts no arguments") diff --git a/d2cli/validate.go b/d2cli/validate.go new file mode 100644 index 000000000..69bd6028e --- /dev/null +++ b/d2cli/validate.go @@ -0,0 +1,41 @@ +package d2cli + +import ( + "context" + "fmt" + + "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/util-go/xdefer" + "oss.terrastruct.com/util-go/xmain" +) + +func validateCmd(ctx context.Context, ms *xmain.State) (err error) { + defer xdefer.Errorf(&err, "") + + ms.Opts = xmain.NewOpts(ms.Env, ms.Opts.Flags.Args()[1:]) + if len(ms.Opts.Args) == 0 { + return xmain.UsageErrorf("input argument required") + } + + inputPath := ms.Opts.Args[0] + if inputPath != "-" { + inputPath = ms.AbsPath(inputPath) + } + + input, err := ms.ReadPath(inputPath) + if err != nil { + return err + } + + _, err = d2lib.Parse(ctx, string(input), nil) + if err != nil { + return err + } + + if inputPath == "-" { + inputPath = "Input" + } + + fmt.Printf("Success! [%s] is valid D2.\n", inputPath) + return nil +} diff --git a/e2etests-cli/main_test.go b/e2etests-cli/main_test.go index dbdcd2833..441fbd986 100644 --- a/e2etests-cli/main_test.go +++ b/e2etests-cli/main_test.go @@ -1346,6 +1346,22 @@ c assert.Success(t, err) }, }, + { + name: "validate-against-correct-d2", + run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) { + writeFile(t, dir, "correct.d2", `x -> y`) + err := runTestMainPersist(t, ctx, dir, env, "validate", "correct.d2") + assert.Success(t, err) + }, + }, + { + name: "validate-against-incorrect-d2", + run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) { + writeFile(t, dir, "incorrect.d2", `x > y`) + err := runTestMainPersist(t, ctx, dir, env, "validate", "incorrect.d2") + assert.Error(t, err) + }, + }, } ctx := context.Background()