diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index 83cf42d57..c62c88718 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -2,9 +2,7 @@ name: daily
on:
workflow_dispatch:
schedule:
- # daily at 00:42 to avoid hourly loads in GitHub actions
- # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
- - cron: '42 0 * * *'
+ - cron: '42 0 * * *' # daily at 00:42
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml
new file mode 100644
index 000000000..3adc6d9a4
--- /dev/null
+++ b/.github/workflows/project.yml
@@ -0,0 +1,15 @@
+name: d2-project
+
+on:
+ issues:
+ types:
+ - opened
+
+jobs:
+ d2-project:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/add-to-project@v0.4.0
+ with:
+ project-url: https://github.com/orgs/terrastruct/projects/34
+ github-token: ${{ secrets._GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 67d08864e..7af7b3572 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
*.got.json
*.got.svg
e2e_report.html
+bin
diff --git a/Makefile b/Makefile
index d4241d86f..fdc039a33 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ all: fmt gen lint build test
.PHONY: fmt
fmt:
- prefix "$@" ./ci/sub/fmt/make.sh
+ prefix "$@" ./ci/fmt.sh
.PHONY: gen
gen:
prefix "$@" ./ci/gen.sh
diff --git a/README.md b/README.md
index db72cafa3..36e20ec24 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
A modern diagram scripting language that turns text to diagrams.
-[Language docs](https://d2lang.com) | [Cheat sheet](./docs/assets/cheat_sheet.pdf)
+[Language docs](https://d2lang.com) | [Cheat sheet](./docs/assets/cheat_sheet.pdf) | [Comparisons](https://text-to-diagram.com)
[](https://github.com/terrastruct/d2/actions/workflows/ci.yml)
[](https://github.com/terrastruct/d2/releases)
@@ -19,28 +19,26 @@
# Table of Contents
+- What does D2 look like?
+- Quickstart
+- Install
+- D2 as a library
+- Themes
+- Fonts
+- Export file types
+- Language tooling
+- Plugins
+- Comparison
+- Contributing
+- License
+- Related
+ - VSCode extension
+ - Vim extension
+ - Language docs
+ - Misc
+- FAQ
-- [What does D2 look like?](#what-does-d2-look-like)
-- [Quickstart](#quickstart)
-- [Install](#install)
-- [D2 as a library](#d2-as-a-library)
-- [Themes](#themes)
-- [Fonts](#fonts)
-- [Export file types](#export-file-types)
-- [Language tooling](#language-tooling)
-- [Plugins](#plugins)
-- [Comparison](#comparison)
-- [Contributing](#contributing)
-- [License](#license)
-- [Related](#related)
- * [VSCode extension](#vscode-extension)
- * [Vim extension](#vim-extension)
- * [Misc](#misc)
-- [FAQ](#faq)
-
-
-
-# What does D2 look like?
+## What does D2 look like?
```d2
# Actors
@@ -97,70 +95,34 @@ The easiest way to install is with our install script:
curl -fsSL https://d2lang.com/install.sh | sh -s --
```
-To uninstall:
+You can run the install script with `--dry-run` to see the commands that will be used
+to install without executing them.
+
+Or if you have Go installed you can install from source though you won't get the manpage:
+
+```sh
+go install oss.terrastruct.com/d2@latest
+```
+
+To uninstall with the install script:
```sh
curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall
```
-For detailed installation docs, with alternative methods and examples for each OS, see
-[./docs/INSTALL.md](./docs/INSTALL.md).
+For detailed installation docs, see [./docs/INSTALL.md](./docs/INSTALL.md).
+We demonstrate alternative methods and examples for each OS.
+
+As well, the functioning of the install script is described in detail to alleviate any
+concern of its use. We recommend using your OS's package manager directly instead for
+improved security but the install script is by no means insecure.
## D2 as a library
In addition to being a runnable CLI tool, D2 can also be used to produce diagrams from
Go programs.
-```go
-import (
- "context"
- "io/ioutil"
- "path/filepath"
- "strings"
-
- "oss.terrastruct.com/d2/d2compiler"
- "oss.terrastruct.com/d2/d2exporter"
- "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
- "oss.terrastruct.com/d2/d2renderers/d2svg"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
- "oss.terrastruct.com/d2/d2themes/d2themescatalog"
-)
-
-func main() {
- graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), &d2compiler.CompileOptions{UTF16: true})
- ruler, _ := textmeasure.NewRuler()
- graph.SetDimensions(nil, ruler)
- d2dagrelayout.Layout(context.Background(), graph)
- diagram, _ := d2exporter.Export(context.Background(), graph, d2themescatalog.NeutralDefault.ID)
- out, _ := d2svg.Render(diagram)
- ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
-}
-```
-
-D2 is built to be hackable -- the language has an API built on top of it to make edits
-programmatically. Modifying the above diagram:
-
-```go
-import (
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
- "oss.terrastruct.com/d2/d2themes/d2themescatalog"
-)
-
-// Create a shape with the ID, "meow"
-graph, _, _ = d2oracle.Create(graph, "meow")
-// Style the shape green
-color := "green"
-graph, _ = d2oracle.Set(graph, "meow.style.fill", nil, &color)
-// Create a shape with the ID, "cat"
-graph, _, _ = d2oracle.Create(graph, "cat")
-// Move the shape "meow" inside the container "cat"
-graph, _ = d2oracle.Move(graph, "meow", "cat.meow")
-// Prints formatted D2 script
-println(d2format.Format(graph.AST))
-```
-
-This makes it easy to build functionality on top of D2. Terrastruct uses the above API to
-implement editing of D2 from mouse actions in a visual interface.
+For examples, see [./docs/examples/lib](./docs/examples/lib).
## Themes
diff --git a/ci/dev.sh b/ci/dev.sh
new file mode 100755
index 000000000..cbc71bf79
--- /dev/null
+++ b/ci/dev.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+cd -- "$(dirname "$0")/.."
+. ./ci/sub/lib.sh
+
+sh_c go build --tags=dev -o=bin/d2 .
+sh_c ./bin/d2 "$@"
diff --git a/ci/fmt.sh b/ci/fmt.sh
new file mode 100755
index 000000000..4fb39da34
--- /dev/null
+++ b/ci/fmt.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -eu
+. "$(dirname "$0")/sub/lib.sh"
+cd -- "$(dirname "$0")/.."
+
+if is_changed README.md; then
+ sh_c tocsubst --skip 1 README.md
+fi
+if is_changed docs/INSTALL.md; then
+ sh_c tocsubst --skip 1 docs/INSTALL.md
+fi
+./ci/sub/fmt/make.sh
diff --git a/ci/release/_build.sh b/ci/release/_build.sh
index c5170219a..4abfd6dec 100755
--- a/ci/release/_build.sh
+++ b/ci/release/_build.sh
@@ -14,7 +14,7 @@ export GOOS=$(goos "$OS")
export GOARCH="$ARCH"
sh_c mkdir -p "$HW_BUILD_DIR/bin"
sh_c go build -ldflags "'-X oss.terrastruct.com/d2/lib/version.Version=$VERSION'" \
- -o "$HW_BUILD_DIR/bin/d2" ./cmd/d2
+ -o "$HW_BUILD_DIR/bin/d2" .
ARCHIVE=$PWD/$ARCHIVE
cd "$(dirname "$HW_BUILD_DIR")"
diff --git a/ci/release/_install.sh b/ci/release/_install.sh
index a66f2d730..510a9c52d 100755
--- a/ci/release/_install.sh
+++ b/ci/release/_install.sh
@@ -91,6 +91,9 @@ note: Deleting the unarchived releases will cause --uninstall to stop working.
You can rerun install.sh to update your version of D2. install.sh will avoid reinstalling
if the installed version is the latest unless --force is passed.
+
+See https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md#security for
+documentation on its security.
EOF
}
@@ -450,13 +453,10 @@ uninstall_tala_brew() {
}
is_prefix_writable() {
- sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true
# The reason for checking whether $INSTALL_DIR is writable is that on macOS you have
# /usr/local owned by root but you don't need root to write to its subdirectories which
# is all we want to do.
- if [ ! -w "$INSTALL_DIR" ]; then
- return 1
- fi
+ is_writable_dir "$INSTALL_DIR"
}
cache_dir() {
@@ -509,4 +509,7 @@ brew() {
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 command brew "$@"
}
+# The main function does more than provide organization. It provides robustness in that if
+# the install script was to only partial download into sh, sh will not execute it because
+# main is not invoked until the very last byte.
main "$@"
diff --git a/ci/release/build.sh b/ci/release/build.sh
index ce2e4eaf4..e42133c24 100755
--- a/ci/release/build.sh
+++ b/ci/release/build.sh
@@ -6,6 +6,7 @@ cd -- "$(dirname "$0")/../.."
help() {
cat </d2---.tar.gz
@@ -38,6 +39,12 @@ Flags:
--lockfile-force
Forcefully take ownership of remote builder lockfiles.
+
+--install
+ Ensure a release using --host-only and install it.
+
+--uninstall
+ Ensure a release using --host-only and uninstall it.
EOF
}
@@ -77,6 +84,18 @@ main() {
flag_noarg && shift "$FLAGSHIFT"
LOCKFILE_FORCE=1
;;
+ install)
+ flag_noarg && shift "$FLAGSHIFT"
+ INSTALL=1
+ HOST_ONLY=1
+ LOCAL=1
+ ;;
+ uninstall)
+ flag_noarg && shift "$FLAGSHIFT"
+ UNINSTALL=1
+ HOST_ONLY=1
+ LOCAL=1
+ ;;
*)
flag_errusage "unrecognized flag $FLAGRAW"
;;
@@ -90,8 +109,12 @@ main() {
VERSION=${VERSION:-$(git_describe_ref)}
BUILD_DIR=ci/release/build/$VERSION
if [ -n "${HOST_ONLY-}" ]; then
- runjob $(os)-$(arch) "OS=$(os) ARCH=$(arch) build" &
- waitjobs
+ runjob $(os)-$(arch) "OS=$(os) ARCH=$(arch) build"
+ if [ -n "${INSTALL-}" ]; then
+ ( sh_c make -sC "ci/release/build/$VERSION/$(os)-$(arch)/d2-$VERSION" install)
+ elif [ -n "${UNINSTALL-}" ]; then
+ ( sh_c make -sC "ci/release/build/$VERSION/$(os)-$(arch)/d2-$VERSION" uninstall)
+ fi
return 0
fi
diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index 9705e0993..6446165c6 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,5 +1,7 @@
-#### Features ๐
+#### Features ๐ธ
+- Formatting of d2 scripts is supported on the CLI with the `fmt` subcommand.
+ [#292](https://github.com/terrastruct/d2/pull/292)
- Latex is now supported. See [docs](https://d2lang.com/tour/text) for more.
[#229](https://github.com/terrastruct/d2/pull/229)
- `direction` keyword is now supported to specify `up`, `down`, `right`, `left` layouts. See
@@ -12,8 +14,10 @@
- Querying shapes and connections by ID is now supported in renders. [#218](https://github.com/terrastruct/d2/pull/218)
- [install.sh](./install.sh) now accepts `-d` as an alias for `--dry-run`.
[#266](https://github.com/terrastruct/d2/pull/266)
+- `-b/--bundle` flag to `d2` now works and bundles all image assets directly as base64
+ data urls. [#278](https://github.com/terrastruct/d2/pull/278)
-#### Improvements ๐ง
+#### Improvements ๐งน
- Local images can now be included, e.g. `icon: ./my_img.png`.
[#146](https://github.com/terrastruct/d2/issues/146)
@@ -21,8 +25,14 @@
[#251](https://github.com/terrastruct/d2/pull/251)
- [install.sh](./install.sh) prints the dry run message more visibly.
[#266](https://github.com/terrastruct/d2/pull/266)
+- `d2` now lives in the root folder of the repository instead of as a subcommand.
+ So you can run `go install oss.terrastruct.com/d2@latest` to install from source
+ now.
+ [#290](https://github.com/terrastruct/d2/pull/290)
+- `BROWSER=0` now works to disable opening a browser on `--watch`.
+ [#311](https://github.com/terrastruct/d2/pull/311)
-#### Bugfixes ๐ด
+#### Bugfixes โ๏ธ
- 3D style was missing border and other styles for its top and right faces.
[#187](https://github.com/terrastruct/d2/pull/187)
@@ -30,5 +40,10 @@
[#159](https://github.com/terrastruct/d2/issues/159)
- Fixes markdown newlines created with a trailing double space or backslash.
[#214](https://github.com/terrastruct/d2/pull/214)
-- Fixes images not loading in PNG exports
+- Fixes images not loading in PNG exports.
[#224](https://github.com/terrastruct/d2/pull/224)
+- Avoid logging benign file watching errors.
+ [#293](https://github.com/terrastruct/d2/pull/293)
+- `$BROWSER` now works to open a custom browser correctly.
+ For example, to open Firefox on macOS: `BROWSER='open -aFirefox'`
+ [#311](https://github.com/terrastruct/d2/pull/311)
diff --git a/ci/release/changelogs/template.md b/ci/release/changelogs/template.md
index dd3f64b05..131061b70 100644
--- a/ci/release/changelogs/template.md
+++ b/ci/release/changelogs/template.md
@@ -1,5 +1,5 @@
-#### Features ๐
+#### Features ๐ธ
-#### Improvements ๐ง
+#### Improvements ๐งน
-#### Bugfixes ๐ด
+#### Bugfixes โ๏ธ
diff --git a/ci/release/gen_template_lib.sh b/ci/release/gen_template_lib.sh
index 1f523767a..27e63ac9e 100755
--- a/ci/release/gen_template_lib.sh
+++ b/ci/release/gen_template_lib.sh
@@ -14,6 +14,7 @@ sh_c cat >./ci/release/template/scripts/lib.sh <\>./ci/release/template/scripts/lib.sh
sh_c chmod -w ./ci/release/template/scripts/lib.sh
diff --git a/ci/release/template/Makefile b/ci/release/template/Makefile
index 5b9ba1353..44f22ddb0 100644
--- a/ci/release/template/Makefile
+++ b/ci/release/template/Makefile
@@ -1,6 +1,11 @@
.POSIX:
.SILENT:
+.PHONY: all
+all:
+ (. ./scripts/lib.sh && echoerr "You must provide a target of install or uninstall for this Makefile")
+ exit 1
+
PREFIX = $(DESTDIR)/usr/local
.PHONY: install
diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1
index 8cf1ce5e2..275eca9da 100644
--- a/ci/release/template/man/d2.1
+++ b/ci/release/template/man/d2.1
@@ -9,16 +9,11 @@
.Op Fl -watch Ar false
.Op Fl -theme Em 0
.Ar file.d2
-.Op Ar file.svg
-|
-.Op Ar file.png
-.Nm d2
-.Op Fl -watch Ar false
-.Op Fl -theme Em 0
-.Ar file.d2
-.Op Ar ...
+.Op Ar file.svg | file.png
.Nm d2
.Ar layout Op Ar name
+.Nm d2
+.Ar fmt Ar file.d2
.Sh DESCRIPTION
.Nm
compiles and renders
@@ -29,10 +24,21 @@ to
.Ar file.png
.Ns .
.Pp
+It defaults to
+.Ar file.svg
+if no output path is passed.
+.Pp
Pass - to have
.Nm
read from stdin or write to stdout.
.Pp
+Never use the presence of the output file to check for success.
+Always use the exit status of
+.Nm d2
+.Ns .
+This is because sometimes when errors occur while rendering, d2 still write out a partial
+render anyway to enable iteration on a broken diagram.
+.Pp
See more docs, the source code and license at
.Lk https://oss.terrastruct.com/d2
.Sh OPTIONS
@@ -71,6 +77,10 @@ Print version information and exit.
Lists available layout engine options with short help.
.It Ar layout Op Ar name
Display long help for a particular layout engine.
+.It Ar fmt Ar file.d2
+Format
+.Ar file.d2
+.Ns .
.El
.Sh SEE ALSO
.Xr d2plugin-tala 1
diff --git a/ci/release/template/scripts/install.sh b/ci/release/template/scripts/install.sh
index 043e5d098..4127f25a8 100755
--- a/ci/release/template/scripts/install.sh
+++ b/ci/release/template/scripts/install.sh
@@ -9,10 +9,15 @@ main() {
return 1
fi
- sh_c mkdir -p "$PREFIX/bin"
- sh_c install ./bin/d2 "$PREFIX/bin/d2"
- sh_c mkdir -p "$PREFIX/share/man/man1"
- sh_c install ./man/d2.1 "$PREFIX/share/man/man1"
+ sh_c="sh_c"
+ if ! is_writable_dir "$PREFIX/bin"; then
+ sh_c="sudo_sh_c"
+ fi
+
+ "$sh_c" mkdir -p "$PREFIX/bin"
+ "$sh_c" install ./bin/d2 "$PREFIX/bin/d2"
+ "$sh_c" mkdir -p "$PREFIX/share/man/man1"
+ "$sh_c" install ./man/d2.1 "$PREFIX/share/man/man1"
}
main "$@"
diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh
index 3dc704bb0..cf17657e7 100644
--- a/ci/release/template/scripts/lib.sh
+++ b/ci/release/template/scripts/lib.sh
@@ -7,6 +7,7 @@
#
# - ./ci/sub/lib/rand.sh
# - ./ci/sub/lib/log.sh
+# - ./ci/sub/lib/release.sh
#
# Generated by ./ci/release/gen_template_lib.sh.
# *************
@@ -55,22 +56,26 @@ tput() {
should_color() {
if [ -n "${COLOR-}" ]; then
- if [ "$COLOR" = 0 -o "$COLOR" = false ]; then
- _COLOR=
- return 1
- elif [ "$COLOR" = 1 -o "$COLOR" = true ]; then
+ if [ "$COLOR" = 1 -o "$COLOR" = true ]; then
_COLOR=1
+ __COLOR=1
return 0
+ elif [ "$COLOR" = 0 -o "$COLOR" = false ]; then
+ _COLOR=
+ __COLOR=0
+ return 1
else
printf '$COLOR must be 0, 1, false or true but got %s\n' "$COLOR" >&2
fi
fi
- if [ -t 1 ]; then
+ if [ -t 1 -a "${TERM-}" != dumb ]; then
_COLOR=1
+ __COLOR=1
return 0
else
_COLOR=
+ __COLOR=0
return 1
fi
}
@@ -89,7 +94,8 @@ _echo() {
get_rand_color() {
# 1-6 are regular and 9-14 are bright.
# 1,2 and 9,10 are red and green but we use those for success and failure.
- pick "$*" 3 4 5 6 11 12 13 14
+ pick "$*" 1 2 3 4 5 6 \
+ 9 10 11 12 13 14
}
echop() {
@@ -113,9 +119,9 @@ printfp() {(
fi
should_color || true
if [ $# -eq 0 ]; then
- printf '%s' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")"
+ printf '%s' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")"
else
- printf '%s: %s\n' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
+ printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
fi
)}
@@ -124,7 +130,7 @@ catp() {
shift
should_color || true
- sed "s/^/$(COLOR=${_COLOR-} printfp "$prefix" '')/"
+ sed "s/^/$(COLOR=$__COLOR printfp "$prefix" '')/"
}
repeat() {
@@ -151,17 +157,17 @@ printferr() {
logp() {
should_color >&2 || true
- COLOR=${_COLOR-} echop "$@" | humanpath >&2
+ COLOR=$__COLOR echop "$@" | humanpath >&2
}
logfp() {
should_color >&2 || true
- COLOR=${_COLOR-} printfp "$@" | humanpath >&2
+ COLOR=$__COLOR printfp "$@" | humanpath >&2
}
logpcat() {
should_color >&2 || true
- COLOR=${_COLOR-} catp "$@" | humanpath >&2
+ COLOR=$__COLOR catp "$@" | humanpath >&2
}
log() {
@@ -286,3 +292,63 @@ runtty() {
return 1
esac
}
+
+capcode() {
+ set +e
+ "$@"
+ code=$?
+ set -e
+}
+#!/bin/sh
+if [ "${LIB_RELEASE-}" ]; then
+ return 0
+fi
+LIB_RELEASE=1
+
+goos() {
+ case $1 in
+ macos) echo darwin ;;
+ *) echo $1 ;;
+ esac
+}
+
+os() {
+ uname=$(uname)
+ case $uname in
+ Linux) echo linux ;;
+ Darwin) echo macos ;;
+ FreeBSD) echo freebsd ;;
+ *) echo "$uname" ;;
+ esac
+}
+
+arch() {
+ uname_m=$(uname -m)
+ case $uname_m in
+ aarch64) echo arm64 ;;
+ x86_64) echo amd64 ;;
+ *) echo "$uname_m" ;;
+ esac
+}
+
+gh_repo() {
+ gh repo view --json nameWithOwner --template '{{ .nameWithOwner }}'
+}
+
+manpath() {
+ if command -v manpath >/dev/null; then
+ command manpath
+ elif man -w 2>/dev/null; then
+ man -w
+ else
+ echo "${MANPATH-}"
+ fi
+}
+
+is_writable_dir() {
+ # The path has to exist for -w to succeed.
+ sh_c "mkdir -p '$1' 2>/dev/null" || true
+ if [ ! -w "$1" ]; then
+ return 1
+ fi
+}
diff --git a/ci/release/template/scripts/uninstall.sh b/ci/release/template/scripts/uninstall.sh
index e21877ca9..56336e8d1 100755
--- a/ci/release/template/scripts/uninstall.sh
+++ b/ci/release/template/scripts/uninstall.sh
@@ -9,8 +9,13 @@ main() {
return 1
fi
- sh_c rm -f "$PREFIX/bin/d2"
- sh_c rm -f "$PREFIX/share/man/man1/d2.1"
+ sh_c="sh_c"
+ if ! is_writable_dir "$PREFIX/bin"; then
+ sh_c="sudo_sh_c"
+ fi
+
+ "$sh_c" rm -f "$PREFIX/bin/d2"
+ "$sh_c" rm -f "$PREFIX/share/man/man1/d2.1"
}
main "$@"
diff --git a/ci/sub b/ci/sub
index 28fb67e3b..70a9ad95e 160000
--- a/ci/sub
+++ b/ci/sub
@@ -1 +1 @@
-Subproject commit 28fb67e3bf11d7df2be9ad57b67b78a1733a7f2d
+Subproject commit 70a9ad95ea0ae1de83fa3b7f7d4a160db4853c20
diff --git a/cmd/d2plugin-dagre/main.go b/cmd/d2plugin-dagre/main.go
index 20cb9af5a..a2350dc4e 100644
--- a/cmd/d2plugin-dagre/main.go
+++ b/cmd/d2plugin-dagre/main.go
@@ -3,8 +3,9 @@
package main
import (
+ "oss.terrastruct.com/util-go/xmain"
+
"oss.terrastruct.com/d2/d2plugin"
- "oss.terrastruct.com/d2/lib/xmain"
)
func main() {
diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go
index fa2cefffa..2ffb448f1 100644
--- a/d2ast/d2ast.go
+++ b/d2ast/d2ast.go
@@ -32,7 +32,7 @@ import (
"unicode/utf16"
"unicode/utf8"
- "oss.terrastruct.com/xdefer"
+ "oss.terrastruct.com/util-go/xdefer"
)
// Node is the base interface implemented by all d2 AST nodes.
diff --git a/d2ast/d2ast_test.go b/d2ast/d2ast_test.go
index 883a68018..b477d7239 100644
--- a/d2ast/d2ast_test.go
+++ b/d2ast/d2ast_test.go
@@ -9,14 +9,16 @@ import (
"strings"
"testing"
- "oss.terrastruct.com/xrand"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/xrand"
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/diff"
+
+ "oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser"
- "oss.terrastruct.com/d2/lib/go2"
)
func TestRange(t *testing.T) {
@@ -193,18 +195,18 @@ func TestRange(t *testing.T) {
var p d2ast.Position
p = p.Advance('a', false)
- diff.AssertJSONEq(t, `"0:1:1"`, p)
+ assert.StringJSON(t, `"0:1:1"`, p)
p = p.Advance('\n', false)
- diff.AssertJSONEq(t, `"1:0:2"`, p)
+ assert.StringJSON(t, `"1:0:2"`, p)
p = p.Advance('รจ', false)
- diff.AssertJSONEq(t, `"1:2:4"`, p)
+ assert.StringJSON(t, `"1:2:4"`, p)
p = p.Advance('๐', false)
- diff.AssertJSONEq(t, `"1:6:8"`, p)
+ assert.StringJSON(t, `"1:6:8"`, p)
p = p.Subtract('๐', false)
- diff.AssertJSONEq(t, `"1:2:4"`, p)
+ assert.StringJSON(t, `"1:2:4"`, p)
p = p.Subtract('รจ', false)
- diff.AssertJSONEq(t, `"1:0:2"`, p)
+ assert.StringJSON(t, `"1:0:2"`, p)
})
t.Run("UTF-16", func(t *testing.T) {
@@ -212,18 +214,18 @@ func TestRange(t *testing.T) {
var p d2ast.Position
p = p.Advance('a', true)
- diff.AssertJSONEq(t, `"0:1:1"`, p)
+ assert.StringJSON(t, `"0:1:1"`, p)
p = p.Advance('\n', true)
- diff.AssertJSONEq(t, `"1:0:2"`, p)
+ assert.StringJSON(t, `"1:0:2"`, p)
p = p.Advance('รจ', true)
- diff.AssertJSONEq(t, `"1:1:3"`, p)
+ assert.StringJSON(t, `"1:1:3"`, p)
p = p.Advance('๐', true)
- diff.AssertJSONEq(t, `"1:3:5"`, p)
+ assert.StringJSON(t, `"1:3:5"`, p)
p = p.Subtract('๐', true)
- diff.AssertJSONEq(t, `"1:1:3"`, p)
+ assert.StringJSON(t, `"1:1:3"`, p)
p = p.Subtract('รจ', true)
- diff.AssertJSONEq(t, `"1:0:2"`, p)
+ assert.StringJSON(t, `"1:0:2"`, p)
})
})
}
@@ -411,7 +413,7 @@ name to "America".
},
}
- diff.AssertJSONEq(t, `{
+ assert.StringJSON(t, `{
"range": "json_test.d2,0:0:0-5:1:50",
"nodes": [
{
@@ -807,7 +809,7 @@ _park `,
t.Parallel()
ast := d2ast.RawString(tc.str, tc.inKey)
- diff.AssertStringEq(t, tc.exp, d2format.Format(ast))
+ assert.String(t, tc.exp, d2format.Format(ast))
})
}
}
diff --git a/d2chaos/d2chaos.go b/d2chaos/d2chaos.go
index 56b8c40b4..8b6c74f5e 100644
--- a/d2chaos/d2chaos.go
+++ b/d2chaos/d2chaos.go
@@ -6,12 +6,13 @@ import (
"strings"
"time"
+ "oss.terrastruct.com/util-go/go2"
+
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2oracle"
"oss.terrastruct.com/d2/d2target"
- "oss.terrastruct.com/d2/lib/go2"
)
func GenDSL(maxi int) (_ string, err error) {
diff --git a/d2chaos/d2chaos_test.go b/d2chaos/d2chaos_test.go
index a73af4d8d..ddbfa9dc2 100644
--- a/d2chaos/d2chaos_test.go
+++ b/d2chaos/d2chaos_test.go
@@ -17,8 +17,8 @@ import (
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2exporter"
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/lib/log"
+ "oss.terrastruct.com/d2/lib/textmeasure"
)
// usage: D2_CHAOS_MAXI=100 D2_CHAOS_N=100 ./ci/test.sh ./d2chaos
diff --git a/d2compiler/compile.go b/d2compiler/compile.go
index b5de7915b..775ced5ec 100644
--- a/d2compiler/compile.go
+++ b/d2compiler/compile.go
@@ -8,12 +8,13 @@ import (
"strconv"
"strings"
+ "oss.terrastruct.com/util-go/go2"
+
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/d2target"
- "oss.terrastruct.com/d2/lib/go2"
)
// TODO: should Parse even be exported? guess not. IR should contain list of files and
diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go
index ea9311d2a..b6c5eba12 100644
--- a/d2compiler/compile_test.go
+++ b/d2compiler/compile_test.go
@@ -6,9 +6,8 @@ import (
"strings"
"testing"
- "oss.terrastruct.com/diff"
-
- "github.com/stretchr/testify/assert"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/diff"
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2format"
@@ -775,8 +774,8 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
- assert.Empty(t, g.Edges[0].Attributes.Shape.Value)
+ assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Shape.Value)
// Make sure the DSL didn't change. this is a regression test where it did
exp := `x -> y: {
source-arrowhead: {
@@ -814,13 +813,13 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
- diff.AssertStringEq(t, "Reisner's Rule of Conceptual Inertia", g.Edges[0].SrcArrowhead.Label.Value)
- diff.AssertStringEq(t, "QOTD", g.Edges[0].DstArrowhead.Label.Value)
- diff.AssertStringEq(t, "true", g.Edges[0].DstArrowhead.Style.Filled.Value)
- assert.Empty(t, g.Edges[0].Attributes.Shape.Value)
- assert.Empty(t, g.Edges[0].Attributes.Label.Value)
- assert.Nil(t, g.Edges[0].Attributes.Style.Filled)
+ assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
+ assert.String(t, "Reisner's Rule of Conceptual Inertia", g.Edges[0].SrcArrowhead.Label.Value)
+ assert.String(t, "QOTD", g.Edges[0].DstArrowhead.Label.Value)
+ assert.String(t, "true", g.Edges[0].DstArrowhead.Style.Filled.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Shape.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Label.Value)
+ assert.JSON(t, nil, g.Edges[0].Attributes.Style.Filled)
},
},
{
@@ -836,8 +835,8 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
- assert.Empty(t, g.Edges[0].Attributes.Shape.Value)
+ assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Shape.Value)
},
},
{
@@ -853,8 +852,8 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "triangle", g.Edges[0].SrcArrowhead.Shape.Value)
- assert.Empty(t, g.Edges[0].Attributes.Shape.Value)
+ assert.String(t, "triangle", g.Edges[0].SrcArrowhead.Shape.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Shape.Value)
},
},
{
@@ -880,8 +879,8 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "yo", g.Edges[0].SrcArrowhead.Label.Value)
- assert.Empty(t, g.Edges[0].Attributes.Label.Value)
+ assert.String(t, "yo", g.Edges[0].SrcArrowhead.Label.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Label.Value)
},
},
{
@@ -899,8 +898,8 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
- assert.Empty(t, g.Edges[0].Attributes.Shape.Value)
+ assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Shape.Value)
},
},
{
@@ -920,9 +919,9 @@ x -> y: {
if len(g.Objects) != 2 {
t.Fatalf("expected 2 objects: %#v", g.Objects)
}
- diff.AssertStringEq(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
- diff.AssertStringEq(t, "diamond", g.Edges[0].DstArrowhead.Shape.Value)
- assert.Empty(t, g.Edges[0].Attributes.Shape.Value)
+ assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
+ assert.String(t, "diamond", g.Edges[0].DstArrowhead.Shape.Value)
+ assert.String(t, "", g.Edges[0].Attributes.Shape.Value)
},
},
{
@@ -1333,7 +1332,7 @@ y -> x.style
if len(g.Objects) != 1 {
t.Fatal(g.Objects)
}
- diff.AssertStringEq(t, `b
+ assert.String(t, `b
b`, g.Objects[0].Attributes.Label.Value)
},
},
@@ -1420,9 +1419,9 @@ b`, g.Objects[0].Attributes.Label.Value)
if len(g.Objects) != 1 {
t.Fatal(g.Objects)
}
- diff.AssertStringEq(t, `field here`, g.Objects[0].Class.Fields[0].Name)
- diff.AssertStringEq(t, `GetType()`, g.Objects[0].Class.Methods[0].Name)
- diff.AssertStringEq(t, `Is()`, g.Objects[0].Class.Methods[1].Name)
+ assert.String(t, `field here`, g.Objects[0].Class.Fields[0].Name)
+ assert.String(t, `GetType()`, g.Objects[0].Class.Methods[0].Name)
+ assert.String(t, `Is()`, g.Objects[0].Class.Methods[1].Name)
},
},
{
@@ -1438,8 +1437,8 @@ b`, g.Objects[0].Attributes.Label.Value)
if len(g.Objects) != 1 {
t.Fatal(g.Objects)
}
- diff.AssertStringEq(t, `GetType()`, g.Objects[0].SQLTable.Columns[0].Name)
- diff.AssertStringEq(t, `Is()`, g.Objects[0].SQLTable.Columns[1].Name)
+ assert.String(t, `GetType()`, g.Objects[0].SQLTable.Columns[0].Name)
+ assert.String(t, `Is()`, g.Objects[0].SQLTable.Columns[1].Name)
},
},
{
@@ -1463,8 +1462,8 @@ b`, g.Objects[0].Attributes.Label.Value)
if len(g.Objects[0].ChildrenArray) != 1 {
t.Fatal(g.Objects)
}
- diff.AssertStringEq(t, `GetType()`, g.Objects[1].SQLTable.Columns[0].Name)
- diff.AssertStringEq(t, `Is()`, g.Objects[1].SQLTable.Columns[1].Name)
+ assert.String(t, `GetType()`, g.Objects[1].SQLTable.Columns[0].Name)
+ assert.String(t, `Is()`, g.Objects[1].SQLTable.Columns[1].Name)
},
},
{
@@ -1509,7 +1508,7 @@ dst.id <-> src.dst_id
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- diff.AssertStringEq(t, "sequence_diagram", g.Objects[0].Attributes.Shape.Value)
+ assert.String(t, "sequence_diagram", g.Objects[0].Attributes.Shape.Value)
},
},
{
@@ -1518,7 +1517,7 @@ dst.id <-> src.dst_id
text: `shape: sequence_diagram
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- diff.AssertStringEq(t, "sequence_diagram", g.Root.Attributes.Shape.Value)
+ assert.String(t, "sequence_diagram", g.Root.Attributes.Shape.Value)
},
},
{
@@ -1526,7 +1525,7 @@ dst.id <-> src.dst_id
text: `direction: right`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- diff.AssertStringEq(t, "right", g.Root.Attributes.Direction.Value)
+ assert.String(t, "right", g.Root.Attributes.Direction.Value)
},
},
{
@@ -1534,7 +1533,7 @@ dst.id <-> src.dst_id
text: `x`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- diff.AssertStringEq(t, "", g.Objects[0].Attributes.Direction.Value)
+ assert.String(t, "", g.Objects[0].Attributes.Direction.Value)
},
},
{
@@ -1544,7 +1543,7 @@ dst.id <-> src.dst_id
direction: left
}`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- diff.AssertStringEq(t, "left", g.Objects[0].Attributes.Direction.Value)
+ assert.String(t, "left", g.Objects[0].Attributes.Direction.Value)
},
},
{
@@ -1594,10 +1593,8 @@ dst.id <-> src.dst_id
Err: err,
}
- err = diff.Testdata(filepath.Join("..", "testdata", "d2compiler", t.Name()), got)
- if err != nil {
- t.Fatal(err)
- }
+ err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2compiler", t.Name()), got)
+ assert.Success(t, err)
})
}
}
diff --git a/d2exporter/export_test.go b/d2exporter/export_test.go
index 1d63d0604..1c08fbe1d 100644
--- a/d2exporter/export_test.go
+++ b/d2exporter/export_test.go
@@ -8,18 +8,17 @@ import (
"cdr.dev/slog"
- "oss.terrastruct.com/diff"
-
- "github.com/stretchr/testify/assert"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/diff"
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2exporter"
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/log"
+ "oss.terrastruct.com/d2/lib/textmeasure"
)
type testCase struct {
@@ -215,10 +214,10 @@ func run(t *testing.T, tc testCase) {
}
ruler, err := textmeasure.NewRuler()
- assert.Nil(t, err)
+ assert.JSON(t, nil, err)
err = g.SetDimensions(nil, ruler)
- assert.Nil(t, err)
+ assert.JSON(t, nil, err)
err = d2dagrelayout.Layout(ctx, g)
if err != nil {
@@ -252,8 +251,6 @@ func run(t *testing.T, tc testCase) {
got.Connections[i].LabelPosition = ""
}
- err = diff.Testdata(filepath.Join("..", "testdata", "d2exporter", t.Name()), got)
- if err != nil {
- t.Fatal(err)
- }
+ err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2exporter", t.Name()), got)
+ assert.Success(t, err)
}
diff --git a/d2format/escape_test.go b/d2format/escape_test.go
index 2d50d6e4a..c0090c03f 100644
--- a/d2format/escape_test.go
+++ b/d2format/escape_test.go
@@ -3,7 +3,7 @@ package d2format_test
import (
"testing"
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
@@ -42,7 +42,7 @@ func TestEscapeSingleQuoted(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- diff.AssertStringEq(t, tc.exp, d2format.Format(&d2ast.SingleQuotedString{
+ assert.String(t, tc.exp, d2format.Format(&d2ast.SingleQuotedString{
Value: tc.str,
}))
})
@@ -104,7 +104,7 @@ func TestEscapeDoubleQuoted(t *testing.T) {
} else {
n = d2ast.FlatDoubleQuotedString(tc.str)
}
- diff.AssertStringEq(t, tc.exp, d2format.Format(n))
+ assert.String(t, tc.exp, d2format.Format(n))
})
}
}
@@ -203,7 +203,7 @@ func TestEscapeUnquoted(t *testing.T) {
n = d2ast.FlatUnquotedString(tc.str)
}
- diff.AssertStringEq(t, tc.exp, d2format.Format(n))
+ assert.String(t, tc.exp, d2format.Format(n))
})
}
}
@@ -286,7 +286,7 @@ func TestEscapeBlockString(t *testing.T) {
Value: tc.value,
}
- diff.AssertStringEq(t, tc.exp, d2format.Format(n))
+ assert.String(t, tc.exp, d2format.Format(n))
})
}
}
diff --git a/d2format/format_test.go b/d2format/format_test.go
index 224154ca3..4bfea1519 100644
--- a/d2format/format_test.go
+++ b/d2format/format_test.go
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser"
@@ -605,7 +605,7 @@ hi # Fraud is the homage that force pays to reason.
if err != nil {
t.Fatal(err)
}
- diff.AssertStringEq(t, tc.exp, d2format.Format(ast))
+ assert.String(t, tc.exp, d2format.Format(ast))
})
}
}
@@ -621,6 +621,6 @@ func TestEdge(t *testing.T) {
t.Fatalf("expected one edge: %#v", mk.Edges)
}
- diff.AssertStringEq(t, `x -> y`, d2format.Format(mk.Edges[0]))
- diff.AssertStringEq(t, `[0]`, d2format.Format(mk.EdgeIndex))
+ assert.String(t, `x -> y`, d2format.Format(mk.Edges[0]))
+ assert.String(t, `[0]`, d2format.Format(mk.EdgeIndex))
}
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index 5e3f5e566..fa5dc3bc6 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -7,16 +7,17 @@ import (
"strconv"
"strings"
+ "oss.terrastruct.com/util-go/go2"
+
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/d2themes"
"oss.terrastruct.com/d2/lib/geo"
- "oss.terrastruct.com/d2/lib/go2"
+ "oss.terrastruct.com/d2/lib/textmeasure"
)
// TODO: Refactor with a light abstract layer on top of AST implementing scenarios,
diff --git a/d2graph/d2graph_test.go b/d2graph/d2graph_test.go
index d207ee89c..b705cb7b4 100644
--- a/d2graph/d2graph_test.go
+++ b/d2graph/d2graph_test.go
@@ -4,7 +4,7 @@ import (
"strings"
"testing"
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2parser"
@@ -44,7 +44,7 @@ func TestKey(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- diff.AssertStringEq(t, tc.exp, strings.Join(d2graph.Key(k), "."))
+ assert.String(t, tc.exp, strings.Join(d2graph.Key(k), "."))
})
}
}
diff --git a/d2graph/serde.go b/d2graph/serde.go
index bfaac7c80..e5a8b1fbc 100644
--- a/d2graph/serde.go
+++ b/d2graph/serde.go
@@ -3,7 +3,7 @@ package d2graph
import (
"encoding/json"
- "oss.terrastruct.com/d2/lib/go2"
+ "oss.terrastruct.com/util-go/go2"
)
type SerializedGraph struct {
diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go
index 672871226..0162820cb 100644
--- a/d2layouts/d2dagrelayout/layout.go
+++ b/d2layouts/d2dagrelayout/layout.go
@@ -11,12 +11,13 @@ import (
"cdr.dev/slog"
v8 "rogchap.com/v8go"
- "oss.terrastruct.com/xdefer"
+ "oss.terrastruct.com/util-go/xdefer"
+
+ "oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/geo"
- "oss.terrastruct.com/d2/lib/go2"
"oss.terrastruct.com/d2/lib/label"
"oss.terrastruct.com/d2/lib/log"
"oss.terrastruct.com/d2/lib/shape"
diff --git a/d2layouts/d2elklayout/layout.go b/d2layouts/d2elklayout/layout.go
index 2df4c65be..e88f01d74 100644
--- a/d2layouts/d2elklayout/layout.go
+++ b/d2layouts/d2elklayout/layout.go
@@ -13,12 +13,13 @@ import (
"rogchap.com/v8go"
- "oss.terrastruct.com/xdefer"
+ "oss.terrastruct.com/util-go/xdefer"
+
+ "oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/geo"
- "oss.terrastruct.com/d2/lib/go2"
"oss.terrastruct.com/d2/lib/label"
)
diff --git a/c.go b/d2lib/c.go
similarity index 90%
rename from c.go
rename to d2lib/c.go
index 1fa3fc225..2774d943e 100644
--- a/c.go
+++ b/d2lib/c.go
@@ -1,6 +1,6 @@
//go:build cgo
-package d2
+package d2lib
import "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
diff --git a/d2.go b/d2lib/d2.go
similarity index 86%
rename from d2.go
rename to d2lib/d2.go
index afa8085fa..54883b47d 100644
--- a/d2.go
+++ b/d2lib/d2.go
@@ -1,4 +1,4 @@
-package d2
+package d2lib
import (
"context"
@@ -9,9 +9,8 @@ import (
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2exporter"
"oss.terrastruct.com/d2/d2graph"
- "oss.terrastruct.com/d2/d2layouts/d2sequence"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
+ "oss.terrastruct.com/d2/lib/textmeasure"
)
type CompileOptions struct {
@@ -23,7 +22,7 @@ type CompileOptions struct {
ThemeID int64
}
-func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target.Diagram, error) {
+func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target.Diagram, *d2graph.Graph, error) {
if opts == nil {
opts = &CompileOptions{}
}
@@ -32,12 +31,12 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target
UTF16: opts.UTF16,
})
if err != nil {
- return nil, err
+ return nil, nil, err
}
err = g.SetDimensions(opts.MeasuredTexts, opts.Ruler)
if err != nil {
- return nil, err
+ return nil, nil, err
}
if layout, err := getLayout(opts); err != nil {
@@ -47,7 +46,7 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target
}
diagram, err := d2exporter.Export(ctx, g, opts.ThemeID)
- return diagram, err
+ return diagram, g, err
}
func getLayout(opts *CompileOptions) (func(context.Context, *d2graph.Graph) error, error) {
diff --git a/d2oracle/edit.go b/d2oracle/edit.go
index 5e943056e..b379df0d5 100644
--- a/d2oracle/edit.go
+++ b/d2oracle/edit.go
@@ -7,9 +7,11 @@ import (
"strings"
"unicode"
- "oss.terrastruct.com/xdefer"
+ "oss.terrastruct.com/util-go/xdefer"
- "oss.terrastruct.com/xrand"
+ "oss.terrastruct.com/util-go/xrand"
+
+ "oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2compiler"
@@ -17,7 +19,6 @@ import (
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/d2target"
- "oss.terrastruct.com/d2/lib/go2"
)
func Create(g *d2graph.Graph, key string) (_ *d2graph.Graph, newKey string, err error) {
diff --git a/d2oracle/edit_test.go b/d2oracle/edit_test.go
index 84090a4e0..7feba4572 100644
--- a/d2oracle/edit_test.go
+++ b/d2oracle/edit_test.go
@@ -7,18 +7,16 @@ import (
"strings"
"testing"
- "github.com/stretchr/testify/assert"
-
- "oss.terrastruct.com/xjson"
-
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/diff"
+ "oss.terrastruct.com/util-go/go2"
+ "oss.terrastruct.com/util-go/xjson"
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2oracle"
"oss.terrastruct.com/d2/d2target"
- "oss.terrastruct.com/d2/lib/go2"
)
// TODO: make assertions less specific
@@ -966,10 +964,10 @@ z: {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, 3, len(g.Objects))
- assert.Equal(t, 1, len(g.Edges))
- assert.Equal(t, "q", g.Edges[0].Src.ID)
- assert.Equal(t, "0.4", g.Edges[0].Attributes.Style.Opacity.Value)
+ assert.JSON(t, 3, len(g.Objects))
+ assert.JSON(t, 1, len(g.Edges))
+ assert.JSON(t, "q", g.Edges[0].Src.ID)
+ assert.JSON(t, "0.4", g.Edges[0].Attributes.Style.Opacity.Value)
},
},
{
@@ -1617,8 +1615,8 @@ func TestMove(t *testing.T) {
exp: `b
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 1)
- assert.Equal(t, g.Objects[0].ID, "b")
+ assert.JSON(t, len(g.Objects), 1)
+ assert.JSON(t, g.Objects[0].ID, "b")
},
},
{
@@ -1636,8 +1634,8 @@ func TestMove(t *testing.T) {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 2)
- assert.Equal(t, g.Objects[1].ID, "c")
+ assert.JSON(t, len(g.Objects), 2)
+ assert.JSON(t, g.Objects[1].ID, "c")
},
},
{
@@ -1692,9 +1690,9 @@ c
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 3)
- assert.Equal(t, "a", g.Objects[0].ID)
- assert.Equal(t, 2, len(g.Objects[0].Children))
+ assert.JSON(t, len(g.Objects), 3)
+ assert.JSON(t, "a", g.Objects[0].ID)
+ assert.JSON(t, 2, len(g.Objects[0].Children))
},
},
{
@@ -1733,9 +1731,9 @@ c
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 2)
- assert.Equal(t, "a", g.Objects[0].ID)
- assert.Equal(t, 1, len(g.Objects[0].Children))
+ assert.JSON(t, len(g.Objects), 2)
+ assert.JSON(t, "a", g.Objects[0].ID)
+ assert.JSON(t, 1, len(g.Objects[0].Children))
},
},
{
@@ -1752,9 +1750,9 @@ c
b
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 2)
- assert.Equal(t, "a", g.Objects[0].ID)
- assert.Equal(t, 0, len(g.Objects[0].Children))
+ assert.JSON(t, len(g.Objects), 2)
+ assert.JSON(t, "a", g.Objects[0].ID)
+ assert.JSON(t, 0, len(g.Objects[0].Children))
},
},
{
@@ -1863,11 +1861,11 @@ c: {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 3)
- assert.Equal(t, "a", g.Objects[0].ID)
- assert.Equal(t, 0, len(g.Objects[0].Children))
- assert.Equal(t, "c", g.Objects[1].ID)
- assert.Equal(t, 1, len(g.Objects[1].Children))
+ assert.JSON(t, len(g.Objects), 3)
+ assert.JSON(t, "a", g.Objects[0].ID)
+ assert.JSON(t, 0, len(g.Objects[0].Children))
+ assert.JSON(t, "c", g.Objects[1].ID)
+ assert.JSON(t, 1, len(g.Objects[1].Children))
},
},
{
@@ -1929,7 +1927,7 @@ a: {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 3)
+ assert.JSON(t, len(g.Objects), 3)
},
},
{
@@ -1986,7 +1984,7 @@ c: {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 3)
+ assert.JSON(t, len(g.Objects), 3)
},
},
{
@@ -2004,7 +2002,7 @@ d: {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 4)
+ assert.JSON(t, len(g.Objects), 4)
},
},
{
@@ -2023,7 +2021,7 @@ c: {
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
- assert.Equal(t, len(g.Objects), 4)
+ assert.JSON(t, len(g.Objects), 4)
},
},
{
@@ -4422,10 +4420,8 @@ func (tc editTest) run(t *testing.T) {
Err: fmt.Sprintf("%#v", err),
}
- err = diff.Testdata(filepath.Join("..", "testdata", "d2oracle", t.Name()), got)
- if err != nil {
- t.Fatal(err)
- }
+ err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2oracle", t.Name()), got)
+ assert.Success(t, err)
}
func TestMoveIDDeltas(t *testing.T) {
@@ -4635,7 +4631,7 @@ x.a -> x.b
t.Fatal(err)
}
- ds, err := diff.Strings(tc.exp, xjson.MarshalIndent(deltas))
+ ds, err := diff.Strings(tc.exp, string(xjson.Marshal(deltas)))
if err != nil {
t.Fatal(err)
}
@@ -4825,7 +4821,7 @@ x.y.z.w.e.p.l -> x.y.z.1.2.3.4
t.Fatal(err)
}
- ds, err := diff.Strings(tc.exp, xjson.MarshalIndent(deltas))
+ ds, err := diff.Strings(tc.exp, string(xjson.Marshal(deltas)))
if err != nil {
t.Fatal(err)
}
@@ -4977,7 +4973,7 @@ x.y.z.w.e.p.l -> x.y.z.1.2.3.4
t.Fatal(err)
}
- ds, err := diff.Strings(tc.exp, xjson.MarshalIndent(deltas))
+ ds, err := diff.Strings(tc.exp, string(xjson.Marshal(deltas)))
if err != nil {
t.Fatal(err)
}
diff --git a/d2parser/parse.go b/d2parser/parse.go
index 3c99dc1eb..d8351dcbc 100644
--- a/d2parser/parse.go
+++ b/d2parser/parse.go
@@ -9,8 +9,9 @@ import (
"unicode"
"unicode/utf8"
+ "oss.terrastruct.com/util-go/go2"
+
"oss.terrastruct.com/d2/d2ast"
- "oss.terrastruct.com/d2/lib/go2"
)
type ParseOptions struct {
diff --git a/d2parser/parse_test.go b/d2parser/parse_test.go
index 9c820e83e..61a02336d 100644
--- a/d2parser/parse_test.go
+++ b/d2parser/parse_test.go
@@ -6,7 +6,8 @@ import (
"strings"
"testing"
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/diff"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2parser"
@@ -382,10 +383,8 @@ q.(x -> y).z: (rawr)
Err: err,
}
- err = diff.Testdata(filepath.Join("..", "testdata", "d2parser", t.Name()), got)
- if err != nil {
- t.Fatal(err)
- }
+ err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2parser", t.Name()), got)
+ assert.Success(t, err)
})
}
}
diff --git a/d2plugin/exec.go b/d2plugin/exec.go
index 467fcdc40..b4929dcc2 100644
--- a/d2plugin/exec.go
+++ b/d2plugin/exec.go
@@ -9,7 +9,7 @@ import (
"os/exec"
"time"
- "oss.terrastruct.com/xdefer"
+ "oss.terrastruct.com/util-go/xdefer"
"oss.terrastruct.com/d2/d2graph"
)
diff --git a/d2plugin/plugin.go b/d2plugin/plugin.go
index 0354b8355..56798c88f 100644
--- a/d2plugin/plugin.go
+++ b/d2plugin/plugin.go
@@ -9,8 +9,9 @@ import (
"context"
"os/exec"
+ "oss.terrastruct.com/util-go/xexec"
+
"oss.terrastruct.com/d2/d2graph"
- "oss.terrastruct.com/d2/lib/xexec"
)
// plugins contains the bundled d2 plugins.
diff --git a/d2plugin/serve.go b/d2plugin/serve.go
index 2db42d215..e7ca429aa 100644
--- a/d2plugin/serve.go
+++ b/d2plugin/serve.go
@@ -7,8 +7,9 @@ import (
"fmt"
"io"
+ "oss.terrastruct.com/util-go/xmain"
+
"oss.terrastruct.com/d2/d2graph"
- "oss.terrastruct.com/d2/lib/xmain"
)
// Serve returns a xmain.RunFunc that will invoke the plugin p as necessary to service the
diff --git a/d2renderers/d2latex/latex.go b/d2renderers/d2latex/latex.go
index 79330ad3a..43a3f9d71 100644
--- a/d2renderers/d2latex/latex.go
+++ b/d2renderers/d2latex/latex.go
@@ -9,7 +9,7 @@ import (
"regexp"
"strconv"
- "oss.terrastruct.com/xdefer"
+ "oss.terrastruct.com/util-go/xdefer"
v8 "rogchap.com/v8go"
)
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index d2f41b9d7..692db2cca 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -20,15 +20,16 @@ import (
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
+ "oss.terrastruct.com/util-go/go2"
+
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/color"
"oss.terrastruct.com/d2/lib/geo"
- "oss.terrastruct.com/d2/lib/go2"
"oss.terrastruct.com/d2/lib/label"
"oss.terrastruct.com/d2/lib/shape"
+ "oss.terrastruct.com/d2/lib/textmeasure"
)
const (
diff --git a/d2target/d2target.go b/d2target/d2target.go
index 3f749fa87..9c2dd2c9b 100644
--- a/d2target/d2target.go
+++ b/d2target/d2target.go
@@ -5,9 +5,10 @@ import (
"net/url"
"strings"
+ "oss.terrastruct.com/util-go/go2"
+
"oss.terrastruct.com/d2/d2themes"
"oss.terrastruct.com/d2/lib/geo"
- "oss.terrastruct.com/d2/lib/go2"
"oss.terrastruct.com/d2/lib/label"
"oss.terrastruct.com/d2/lib/shape"
)
diff --git a/docs/INSTALL.md b/docs/INSTALL.md
index 71d3fcd0a..97ec69d1b 100644
--- a/docs/INSTALL.md
+++ b/docs/INSTALL.md
@@ -1,15 +1,16 @@
# install
-You may install D2 through any of the following methods.
+You may install `d2` through any of the following methods.
-
-- [install.sh](#installsh)
-- [macOS (Homebrew)](#macos-homebrew)
-- [Standalone](#standalone)
-- [From source](#from-source)
-
-
+- install.sh
+ - Security
+- macOS (Homebrew)
+- Standalone
+ - Manual
+ - PREFIX
+- From source
+- Coming soon
## install.sh
@@ -31,6 +32,36 @@ methods:
curl -fsSL https://d2lang.com/install.sh | sh -s -- --help
```
+### Security
+
+The install script is not the most secure way to install d2. We recommend that if
+possible, you use your OS's package manager directly or install from source with `go` as
+described below.
+
+But this does not mean the install script is insecure. There is no major flaw that
+the install script is more vulnerable to than any other method of manual installation.
+The most secure installation method involves a second independent entity, i.e your OS
+package repos or Go's proxy server.
+
+We're careful shell programmers and are aware of the many footguns of the Unix shell. Our
+script was written carefully and with detail. For example, it is not vulnerable to partial
+execution and the entire script runs with `set -eu` and very meticulous quoting.
+
+It follows the XDG standards, installs `d2` properly into a Unix hierarchy path (defaulting
+to /usr/local though you can use ~/.local to avoid sudo if you'd like) and allows for easy
+uninstall.
+
+Some other niceties are that it'll tell you if you need to adjust `$PATH` or `$MANPATH` to
+access `d2` and its manpages. It can also install
+[TALA](https://github.com/terrastruct/tala) for you with `--tala`. You can also use it to
+install a specific version of `d2` with `--version`. Run it with `--help` for more more
+detailed docs on its various options and features.
+
+If you're still concerned, remember you can run with `--dry-run` to avoid writing anything.
+
+The install script does not yet verify any signature on the downloaded release
+but that is coming soon. [#315](https://github.com/terrastruct/d2/issues/315)
+
## macOS (Homebrew)
If you're on macOS, you can install with `brew`.
@@ -46,8 +77,28 @@ brew install d2
## Standalone
We publish standalone release archives for every release on Github.
-Download the `.tar.gz` release for your OS/ARCH combination and then run the following
-inside the extracted directory to install:
+
+Here's a minimal example script that downloads a standalone release, extracts it into the
+current directory and then installs it.
+Adjust VERSION, OS, and ARCH as needed.
+
+```sh
+VERSION=v0.0.13 OS=macos ARCH=amd64 curl -fsSLO \
+ "https://github.com/terrastruct/d2/releases/download/$VERSION/d2-$VERSION-$OS-$ARCH.tar.gz" \
+ && tar -xzf "d2-$VERSION-$OS-$ARCH.tar.gz" \
+ && make -sC "d2-$VERSION" install
+```
+
+To uninstall:
+
+```sh
+VERSION=v0.0.13 make -sC "d2-$VERSION" uninstall
+```
+
+### Manual
+
+You can also manually download the `.tar.gz` release for your OS/ARCH combination and then
+run the following inside the extracted directory to install:
```sh
make install
@@ -59,10 +110,11 @@ Run the following to uninstall:
make uninstall
```
-If root permissions are required for installation, you'll need to run `make` with `sudo`.
+### PREFIX
+
You can control the Unix hierarchy installation path with `PREFIX=`. For example:
-```
+```sh
# Install under ~/.local.
# Binaries will be at ~/.local/bin
# And manpages will be under ~/.local/share/man
@@ -83,7 +135,15 @@ know where the release directory is for easy uninstall.
You can always install from source:
```sh
-go install oss.terrastruct.com/d2/cmd/d2@latest
+go install oss.terrastruct.com/d2@latest
+```
+
+To install a proper release from source clone the repository and then:
+
+```sh
+./ci/release/build.sh --install
+# To uninstall:
+# ./ci/release/build.sh --uninstall
```
## Coming soon
diff --git a/docs/examples/lib/1-d2lib/.gitignore b/docs/examples/lib/1-d2lib/.gitignore
new file mode 100644
index 000000000..958df5ba9
--- /dev/null
+++ b/docs/examples/lib/1-d2lib/.gitignore
@@ -0,0 +1 @@
+out.svg
diff --git a/docs/examples/lib/1-d2lib/d2lib.go b/docs/examples/lib/1-d2lib/d2lib.go
new file mode 100644
index 000000000..a28b97f07
--- /dev/null
+++ b/docs/examples/lib/1-d2lib/d2lib.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+ "context"
+ "io/ioutil"
+ "path/filepath"
+
+ "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
+ "oss.terrastruct.com/d2/d2lib"
+ "oss.terrastruct.com/d2/d2renderers/d2svg"
+ "oss.terrastruct.com/d2/d2themes/d2themescatalog"
+ "oss.terrastruct.com/d2/lib/textmeasure"
+)
+
+// Remember to add if err != nil checks in production.
+func main() {
+ ruler, _ := textmeasure.NewRuler()
+ diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
+ Layout: d2dagrelayout.Layout,
+ Ruler: ruler,
+ ThemeID: d2themescatalog.GrapeSoda.ID,
+ })
+ out, _ := d2svg.Render(diagram)
+ _ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
+}
diff --git a/docs/examples/lib/1-d2lib/d2lib_test.go b/docs/examples/lib/1-d2lib/d2lib_test.go
new file mode 100644
index 000000000..53b3d5bcc
--- /dev/null
+++ b/docs/examples/lib/1-d2lib/d2lib_test.go
@@ -0,0 +1,9 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestMain_(t *testing.T) {
+ main()
+}
diff --git a/docs/examples/lib/2-d2oracle/d2oracle.go b/docs/examples/lib/2-d2oracle/d2oracle.go
new file mode 100644
index 000000000..1f265a05e
--- /dev/null
+++ b/docs/examples/lib/2-d2oracle/d2oracle.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "context"
+ "fmt"
+
+ "oss.terrastruct.com/d2/d2format"
+ "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
+ "oss.terrastruct.com/d2/d2lib"
+ "oss.terrastruct.com/d2/d2oracle"
+ "oss.terrastruct.com/d2/d2themes/d2themescatalog"
+ "oss.terrastruct.com/d2/lib/textmeasure"
+)
+
+// Remember to add if err != nil checks in production.
+func main() {
+ // From one.go
+ ruler, _ := textmeasure.NewRuler()
+ _, graph, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
+ Layout: d2dagrelayout.Layout,
+ Ruler: ruler,
+ ThemeID: d2themescatalog.GrapeSoda.ID,
+ })
+
+ // Create a shape with the ID, "meow"
+ graph, _, _ = d2oracle.Create(graph, "meow")
+ // Style the shape green
+ color := "green"
+ graph, _ = d2oracle.Set(graph, "meow.style.fill", nil, &color)
+ // Create a shape with the ID, "cat"
+ graph, _, _ = d2oracle.Create(graph, "cat")
+ // Move the shape "meow" inside the container "cat"
+ graph, _ = d2oracle.Move(graph, "meow", "cat.meow")
+ // Prints formatted D2 script
+ fmt.Print(d2format.Format(graph.AST))
+}
diff --git a/docs/examples/lib/2-d2oracle/d2oracle_test.go b/docs/examples/lib/2-d2oracle/d2oracle_test.go
new file mode 100644
index 000000000..53b3d5bcc
--- /dev/null
+++ b/docs/examples/lib/2-d2oracle/d2oracle_test.go
@@ -0,0 +1,9 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestMain_(t *testing.T) {
+ main()
+}
diff --git a/docs/examples/lib/3-lowlevel/.gitignore b/docs/examples/lib/3-lowlevel/.gitignore
new file mode 100644
index 000000000..958df5ba9
--- /dev/null
+++ b/docs/examples/lib/3-lowlevel/.gitignore
@@ -0,0 +1 @@
+out.svg
diff --git a/docs/examples/lib/3-lowlevel/lowlevel.go b/docs/examples/lib/3-lowlevel/lowlevel.go
new file mode 100644
index 000000000..e8f7a70a8
--- /dev/null
+++ b/docs/examples/lib/3-lowlevel/lowlevel.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "context"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+
+ "oss.terrastruct.com/d2/d2compiler"
+ "oss.terrastruct.com/d2/d2exporter"
+ "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
+ "oss.terrastruct.com/d2/d2renderers/d2svg"
+ "oss.terrastruct.com/d2/d2themes/d2themescatalog"
+ "oss.terrastruct.com/d2/lib/textmeasure"
+)
+
+// Remember to add if err != nil checks in production.
+func main() {
+ graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
+ ruler, _ := textmeasure.NewRuler()
+ _ = graph.SetDimensions(nil, ruler)
+ _ = d2dagrelayout.Layout(context.Background(), graph)
+ diagram, _ := d2exporter.Export(context.Background(), graph, d2themescatalog.NeutralDefault.ID)
+ out, _ := d2svg.Render(diagram)
+ _ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
+}
diff --git a/docs/examples/lib/3-lowlevel/lowlevel_test.go b/docs/examples/lib/3-lowlevel/lowlevel_test.go
new file mode 100644
index 000000000..53b3d5bcc
--- /dev/null
+++ b/docs/examples/lib/3-lowlevel/lowlevel_test.go
@@ -0,0 +1,9 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestMain_(t *testing.T) {
+ main()
+}
diff --git a/docs/examples/lib/README.md b/docs/examples/lib/README.md
new file mode 100644
index 000000000..979e9af7e
--- /dev/null
+++ b/docs/examples/lib/README.md
@@ -0,0 +1,30 @@
+# D2 library examples
+
+We have a few examples in this directory on how to use the D2 library to turn D2 scripts
+into rendered svg diagrams and more.
+
+Each example is runnable though does not include error handling for readability.
+
+### [./1-d2lib](./1-d2lib)
+
+A minimal example showing you how to compile the diagram `x -> y` into an svg.
+
+### [./2-d2oracle](./2-d2oracle)
+
+D2 is built to be hackable -- the language has an API built on top of it to make edits
+programmatically.
+
+Modifying the previous example, this example demonstrates how
+[d2oracle](../../../d2oracle) can be used to create a new shape, style it programatically
+and then output the modified d2 script.
+
+This makes it easy to build functionality on top of D2. Terrastruct uses the
+[d2oracle](../../../d2oracle) API to implement editing of D2 from mouse actions in a
+visual interface.
+
+### [./3-lowlevel](./3-lowlevel)
+
+`d2lib` from the first example is just a wrapper around the lower level APIs. They
+can be used directly and this example demonstrates such usage.
+
+This shouldn't be necessary for most usecases.
diff --git a/e2etests/e2e_test.go b/e2etests/e2e_test.go
index 513451247..567008cd6 100644
--- a/e2etests/e2e_test.go
+++ b/e2etests/e2e_test.go
@@ -12,19 +12,19 @@ import (
"cdr.dev/slog"
- "github.com/stretchr/testify/assert"
+ tassert "github.com/stretchr/testify/assert"
- "oss.terrastruct.com/diff"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/diff"
- "oss.terrastruct.com/d2"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
+ "oss.terrastruct.com/d2/d2lib"
"oss.terrastruct.com/d2/d2renderers/d2svg"
- "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
- xdiff "oss.terrastruct.com/d2/lib/diff"
"oss.terrastruct.com/d2/lib/log"
+ "oss.terrastruct.com/d2/lib/textmeasure"
)
func TestE2E(t *testing.T) {
@@ -90,7 +90,7 @@ func run(t *testing.T, tc testCase) {
ctx = log.Leveled(ctx, slog.LevelDebug)
ruler, err := textmeasure.NewRuler()
- if !assert.Nil(t, err) {
+ if !tassert.Nil(t, err) {
return
}
@@ -104,13 +104,13 @@ func run(t *testing.T, tc testCase) {
} else if layoutName == "elk" {
layout = d2elklayout.Layout
}
- diagram, err := d2.Compile(ctx, tc.script, &d2.CompileOptions{
+ diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
UTF16: true,
Ruler: ruler,
ThemeID: 0,
Layout: layout,
})
- if !assert.Nil(t, err) {
+ if !tassert.Nil(t, err) {
return
}
@@ -122,34 +122,23 @@ func run(t *testing.T, tc testCase) {
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestE2E/"), layoutName)
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
- pathExpSVG := filepath.Join(dataPath, "sketch.exp.svg")
+
svgBytes, err := d2svg.Render(diagram)
- if err != nil {
- t.Fatal(err)
- }
+ assert.Success(t, err)
+ err = ioutil.WriteFile(pathGotSVG, svgBytes, 0600)
+ assert.Success(t, err)
+ defer os.Remove(pathGotSVG)
var xmlParsed interface{}
- if err := xml.Unmarshal(svgBytes, &xmlParsed); err != nil {
- t.Fatalf("invalid SVG: %v", err)
- }
+ err = xml.Unmarshal(svgBytes, &xmlParsed)
+ assert.Success(t, err)
- err = diff.Testdata(filepath.Join(dataPath, "board"), diagram)
- if err != nil {
- ioutil.WriteFile(pathGotSVG, svgBytes, 0600)
- t.Fatal(err)
- }
+ err = diff.TestdataJSON(filepath.Join(dataPath, "board"), diagram)
+ assert.Success(t, err)
if os.Getenv("SKIP_SVG_CHECK") == "" {
- err = xdiff.TestdataGeneric(filepath.Join(dataPath, "sketch"), ".svg", svgBytes)
- if err != nil {
- ioutil.WriteFile(pathGotSVG, svgBytes, 0600)
- t.Fatal(err)
- }
+ err = diff.Testdata(filepath.Join(dataPath, "sketch"), ".svg", svgBytes)
+ assert.Success(t, err)
}
- err = ioutil.WriteFile(pathExpSVG, svgBytes, 0600)
- if err != nil {
- t.Fatal(err)
- }
- os.Remove(filepath.Join(dataPath, "sketch.got.svg"))
}
}
diff --git a/fmt.go b/fmt.go
new file mode 100644
index 000000000..e3a755a79
--- /dev/null
+++ b/fmt.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "bytes"
+ "context"
+
+ "oss.terrastruct.com/util-go/xdefer"
+
+ "oss.terrastruct.com/util-go/xmain"
+
+ "oss.terrastruct.com/d2/d2format"
+ "oss.terrastruct.com/d2/d2parser"
+)
+
+func fmtCmd(ctx context.Context, ms *xmain.State) (err error) {
+ defer xdefer.Errorf(&err, "failed to fmt")
+
+ ms.Opts = xmain.NewOpts(ms.Env, ms.Log, ms.Opts.Flags.Args()[1:])
+ if len(ms.Opts.Args) == 0 {
+ return xmain.UsageErrorf("fmt must be passed the file to be formatted")
+ } else if len(ms.Opts.Args) > 1 {
+ return xmain.UsageErrorf("fmt accepts only one argument for the file to be formatted")
+ }
+
+ inputPath := ms.Opts.Args[0]
+ input, err := ms.ReadPath(inputPath)
+ if err != nil {
+ return err
+ }
+
+ m, err := d2parser.Parse(inputPath, bytes.NewReader(input), nil)
+ if err != nil {
+ return err
+ }
+
+ return ms.WritePath(inputPath, []byte(d2format.Format(m)))
+}
diff --git a/go.mod b/go.mod
index fc891de0f..6ac33f5bd 100644
--- a/go.mod
+++ b/go.mod
@@ -10,25 +10,17 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mazznoer/csscolorparser v0.1.3
- github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/playwright-community/playwright-go v0.2000.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.1
github.com/yuin/goldmark v1.5.3
go.uber.org/multierr v1.8.0
- golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9
golang.org/x/image v0.1.0
golang.org/x/net v0.2.0
- golang.org/x/text v0.4.0
+ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
gonum.org/v1/plot v0.12.0
nhooyr.io/websocket v1.8.7
- oss.terrastruct.com/cmdlog v0.0.0-20221129200109-540ef52ff07d
- oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541
- oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f
- oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557
- oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a
- oss.terrastruct.com/xos v0.0.0-20221018030138-c96e7ae96e5d
- oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333
+ oss.terrastruct.com/util-go v0.0.0-20221201191904-5edc89ce397b
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95
)
@@ -49,15 +41,17 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.3.0 // indirect
+ golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/term v0.2.0 // indirect
- golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
+ golang.org/x/text v0.4.0 // indirect
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
diff --git a/go.sum b/go.sum
index 9eca1dfde..22827dd80 100644
--- a/go.sum
+++ b/go.sum
@@ -798,20 +798,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
-oss.terrastruct.com/cmdlog v0.0.0-20221129200109-540ef52ff07d h1:oc3cqW4bWaosVpfM8yNLAPSDMjLrVYP2ztF7w3tRvws=
-oss.terrastruct.com/cmdlog v0.0.0-20221129200109-540ef52ff07d/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg=
-oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541 h1:I9B1O1IJ6spivIQxbFRZmbhAwVeLwrcQRR1JbYUOvrI=
-oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541/go.mod h1:ags2QDy/T6jr69hT6bpmAmhr2H98n9o8Atf3QlUJPiU=
-oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f h1:7voRCwKM7TZkTo9u7hj+uV/zXoVB8czWrTq6MVIh3dg=
-oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f/go.mod h1:Y0coTLsWwX0q3a+/Ndq797t+vWyxm42T49Ik3bzaDKY=
-oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557 h1:rPbhJbN1q7B4tnppSPoAMwq0t6Pk5SrQDQ5S6uoNNHg=
-oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557/go.mod h1:plvfydF5METAlsbpeuSz44jckaOwrCWX3M0kTLoCA4I=
-oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a h1:AAcupsjBwpbcyLASX0ppDlxbfHWb5Neq5gWdGpLfaSA=
-oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a/go.mod h1:XJ71qiTzk/dbTWuYbuLJuRpBdKFN06Sk5FdFpq2TNmE=
-oss.terrastruct.com/xos v0.0.0-20221018030138-c96e7ae96e5d h1:rrPTkbAfsRTW1WLoTzEofS9AprsHovy9bwvA/wC8Dys=
-oss.terrastruct.com/xos v0.0.0-20221018030138-c96e7ae96e5d/go.mod h1:uSONPDInIwglnC+0zYs8YOjiUD8ZUSnqDTTI82j7Oro=
-oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333 h1:7EdxwXM75Id1VIN71QbE8bLzZRMs0qD7olnDw5gbI7w=
-oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333/go.mod h1:O7TAoBmlQhoi46RdgVikDcoLRb/vLflhkXCAd+nO4SM=
+oss.terrastruct.com/util-go v0.0.0-20221201191904-5edc89ce397b h1:o8+5KfZpQyaw7uKcPIdc9HOqVjVDEdsPZpdRV1k0rmc=
+oss.terrastruct.com/util-go v0.0.0-20221201191904-5edc89ce397b/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95 h1:r89YHVIWeQj/A3Nu6462eqARUECJlJkLRk36pfML1xA=
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/cmd/d2/help.go b/help.go
similarity index 85%
rename from cmd/d2/help.go
rename to help.go
index 98390495d..bd6b455d0 100644
--- a/cmd/d2/help.go
+++ b/help.go
@@ -9,29 +9,37 @@ import (
"path/filepath"
"strings"
+ "oss.terrastruct.com/util-go/xmain"
+
"oss.terrastruct.com/d2/d2plugin"
- "oss.terrastruct.com/d2/lib/xmain"
)
func help(ms *xmain.State) {
fmt.Fprintf(ms.Stdout, `Usage:
- %s [--watch=false] [--theme=0] file.d2 [file.svg|file.png]
+ %[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png]
+ %[1]s layout [name]
+ %[1]s fmt 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.
-%[1]s compiles and renders file.d2 to file.svg|file.png.
Use - to have d2 read from stdin or write to stdout.
+See man d2 for more detailed docs.
+
Flags:
%s
Subcommands:
%[1]s layout - Lists available layout engine options with short help
- %[1]s layout [layout name] - Display long help for a particular layout engine
+ %[1]s layout [name] - Display long help for a particular layout engine
+ %[1]s fmt file.d2 - Format file.d2
See more docs and the source code at https://oss.terrastruct.com/d2
-`, ms.Name, ms.Opts.Defaults())
+`, filepath.Base(ms.Name), ms.Opts.Defaults())
}
-func layoutHelp(ctx context.Context, ms *xmain.State) error {
+func layoutCmd(ctx context.Context, ms *xmain.State) error {
if len(ms.Opts.Flags.Args()) == 1 {
return shortLayoutHelp(ctx, ms)
} else if len(ms.Opts.Flags.Args()) == 2 {
diff --git a/install.sh b/install.sh
index 7afad1716..f6f1c4620 100755
--- a/install.sh
+++ b/install.sh
@@ -61,22 +61,26 @@ tput() {
should_color() {
if [ -n "${COLOR-}" ]; then
- if [ "$COLOR" = 0 -o "$COLOR" = false ]; then
- _COLOR=
- return 1
- elif [ "$COLOR" = 1 -o "$COLOR" = true ]; then
+ if [ "$COLOR" = 1 -o "$COLOR" = true ]; then
_COLOR=1
+ __COLOR=1
return 0
+ elif [ "$COLOR" = 0 -o "$COLOR" = false ]; then
+ _COLOR=
+ __COLOR=0
+ return 1
else
printf '$COLOR must be 0, 1, false or true but got %s\n' "$COLOR" >&2
fi
fi
- if [ -t 1 ]; then
+ if [ -t 1 -a "${TERM-}" != dumb ]; then
_COLOR=1
+ __COLOR=1
return 0
else
_COLOR=
+ __COLOR=0
return 1
fi
}
@@ -95,7 +99,8 @@ _echo() {
get_rand_color() {
# 1-6 are regular and 9-14 are bright.
# 1,2 and 9,10 are red and green but we use those for success and failure.
- pick "$*" 3 4 5 6 11 12 13 14
+ pick "$*" 1 2 3 4 5 6 \
+ 9 10 11 12 13 14
}
echop() {
@@ -119,9 +124,9 @@ printfp() {(
fi
should_color || true
if [ $# -eq 0 ]; then
- printf '%s' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")"
+ printf '%s' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")"
else
- printf '%s: %s\n' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
+ printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$FGCOLOR" "$prefix")" "$(printf "$@")"
fi
)}
@@ -130,7 +135,7 @@ catp() {
shift
should_color || true
- sed "s/^/$(COLOR=${_COLOR-} printfp "$prefix" '')/"
+ sed "s/^/$(COLOR=$__COLOR printfp "$prefix" '')/"
}
repeat() {
@@ -157,17 +162,17 @@ printferr() {
logp() {
should_color >&2 || true
- COLOR=${_COLOR-} echop "$@" | humanpath >&2
+ COLOR=$__COLOR echop "$@" | humanpath >&2
}
logfp() {
should_color >&2 || true
- COLOR=${_COLOR-} printfp "$@" | humanpath >&2
+ COLOR=$__COLOR printfp "$@" | humanpath >&2
}
logpcat() {
should_color >&2 || true
- COLOR=${_COLOR-} catp "$@" | humanpath >&2
+ COLOR=$__COLOR catp "$@" | humanpath >&2
}
log() {
@@ -292,6 +297,13 @@ runtty() {
return 1
esac
}
+
+capcode() {
+ set +e
+ "$@"
+ code=$?
+ set -e
+}
#!/bin/sh
if [ "${LIB_FLAG-}" ]; then
return 0
@@ -468,6 +480,14 @@ manpath() {
echo "${MANPATH-}"
fi
}
+
+is_writable_dir() {
+ # The path has to exist for -w to succeed.
+ sh_c "mkdir -p '$1' 2>/dev/null" || true
+ if [ ! -w "$1" ]; then
+ return 1
+ fi
+}
#!/bin/sh
set -eu
@@ -556,6 +576,9 @@ note: Deleting the unarchived releases will cause --uninstall to stop working.
You can rerun install.sh to update your version of D2. install.sh will avoid reinstalling
if the installed version is the latest unless --force is passed.
+
+See https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md#security for
+documentation on its security.
EOF
}
@@ -915,13 +938,10 @@ uninstall_tala_brew() {
}
is_prefix_writable() {
- sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true
# The reason for checking whether $INSTALL_DIR is writable is that on macOS you have
# /usr/local owned by root but you don't need root to write to its subdirectories which
# is all we want to do.
- if [ ! -w "$INSTALL_DIR" ]; then
- return 1
- fi
+ is_writable_dir "$INSTALL_DIR"
}
cache_dir() {
@@ -974,4 +994,7 @@ brew() {
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 command brew "$@"
}
+# The main function does more than provide organization. It provides robustness in that if
+# the install script was to only partial download into sh, sh will not execute it because
+# main is not invoked until the very last byte.
main "$@"
diff --git a/lib/compress/compress_test.go b/lib/compress/compress_test.go
deleted file mode 100644
index b1fe82e12..000000000
--- a/lib/compress/compress_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package compress
-
-import (
- "testing"
-
- "oss.terrastruct.com/diff"
-)
-
-func TestCompression(t *testing.T) {
- script := `x -> y
-I just forgot my whole philosophy of life!!!: {
- s: TV is chewing gum for the eyes
-}
-`
-
- encoded, err := Compress(script)
- if err != nil {
- t.Fatal(err)
- }
-
- decoded, err := Decompress(encoded)
- if err != nil {
- t.Fatal(err)
- }
-
- diff.AssertStringEq(t, script, decoded)
-}
diff --git a/lib/diff/diff.go b/lib/diff/diff.go
deleted file mode 100644
index 43204e272..000000000
--- a/lib/diff/diff.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package diff
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
-
- "oss.terrastruct.com/diff"
-)
-
-// TODO refactor with diff repo
-func TestdataGeneric(path, fileExtension string, got []byte) (err error) {
- expPath := fmt.Sprintf("%s.exp%s", path, fileExtension)
- gotPath := fmt.Sprintf("%s.got%s", path, fileExtension)
-
- err = os.MkdirAll(filepath.Dir(gotPath), 0755)
- if err != nil {
- return err
- }
- err = ioutil.WriteFile(gotPath, got, 0600)
- if err != nil {
- return err
- }
-
- ds, err := diff.Files(expPath, gotPath)
- if err != nil {
- return err
- }
-
- if ds != "" {
- if os.Getenv("TESTDATA_ACCEPT") != "" {
- return os.Rename(gotPath, expPath)
- }
- return fmt.Errorf("diff (rerun with $TESTDATA_ACCEPT=1 to accept):\n%s", ds)
- }
- return os.Remove(gotPath)
-}
diff --git a/lib/go2/go2.go b/lib/go2/go2.go
deleted file mode 100644
index 20482efa2..000000000
--- a/lib/go2/go2.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Package go2 contains general utility helpers that should've been in Go. Maybe they'll be in Go 2.0.
-package go2
-
-import (
- "hash/fnv"
- "math"
-
- "golang.org/x/exp/constraints"
-)
-
-func Pointer[T any](v T) *T {
- return &v
-}
-
-func Min[T constraints.Ordered](a, b T) T {
- if a < b {
- return a
- }
- return b
-}
-
-func Max[T constraints.Ordered](a, b T) T {
- if a > b {
- return a
- }
- return b
-}
-
-func StringToIntHash(s string) int {
- h := fnv.New32a()
- h.Write([]byte(s))
- return int(h.Sum32())
-}
-
-func Contains[T comparable](els []T, el T) bool {
- for _, el2 := range els {
- if el2 == el {
- return true
- }
- }
- return false
-}
-
-func Filter[T any](els []T, fn func(T) bool) []T {
- out := []T{}
- for _, el := range els {
- if fn(el) {
- out = append(out, el)
- }
- }
- return out
-}
-
-func IntMax(x, y int) int {
- return int(math.Max(float64(x), float64(y)))
-}
-
-func IntMin(x, y int) int {
- return int(math.Min(float64(x), float64(y)))
-}
diff --git a/lib/imgbundler/imgbundler.go b/lib/imgbundler/imgbundler.go
index 82e7105b9..14ff92cd8 100644
--- a/lib/imgbundler/imgbundler.go
+++ b/lib/imgbundler/imgbundler.go
@@ -6,156 +6,204 @@ import (
"encoding/base64"
"fmt"
"io/ioutil"
+ "mime"
"net/http"
"net/url"
"os"
+ "path"
"regexp"
"strings"
"sync"
"time"
- "go.uber.org/multierr"
- "oss.terrastruct.com/xdefer"
+ "golang.org/x/xerrors"
+ "oss.terrastruct.com/util-go/xdefer"
- "oss.terrastruct.com/d2/lib/xmain"
+ "oss.terrastruct.com/util-go/xmain"
)
const maxImageSize int64 = 1 << 25 // 33_554_432
-var imageRe = regexp.MustCompile(` 0 {
+ return svg, xerrors.Errorf("%v", errhrefs)
+ }
+ return svg, nil
}
- if resp.err != nil {
- err = multierr.Combine(err, resp.err)
- continue
- }
- svg = bytes.Replace(svg, []byte(resp.srctxt), []byte(fmt.Sprintf(``, href)
- matches := imageRe.FindAllStringSubmatch(str, -1)
+ matches := imageRegex.FindAllStringSubmatch(str, -1)
if len(matches) != 1 {
t.Fatalf("uri regex didn't match %s", str)
}
@@ -88,9 +88,9 @@ width="328" height="587" viewBox="-100 -131 328 587">