@ 261101.7

261026.5 · e0e318febdcbbba34e53f28d52928a8ba9a2206e

JSON files have a gron view

A code page for a .json file offers gron at the left end of the view row. It shows one line per node as a JS assignment, eg json.tags[0] = "a";, so each value reads and searches with its full path. The conversion runs in-process, scans once and builds no tree. Keys keep input order and values show as written. A file that is not valid JSON shows the flat code under a line naming where parsing stopped.

reading declarations

Changes to gron_test.go. All files

gron_test.go

new file mode 100644@@ -0,0 +1,81 @@+package main++import (+	"os"+	"path/filepath"+	"strings"+	"testing"+)++func TestGron(t *testing.T) {+	in := `{"name":"Anna","n":-1.5e3,"ok":true,"x":null,"tags":["a",[1,{}]],"meta":{},+"createdAt":"2026","Content-Type":"x","class":1,"$v":2,"åke":"ö","escA":"v","1st":0}`+	want := `json = {};+json.name = "Anna";+json.n = -1.5e3;+json.ok = true;+json.x = null;+json.tags = [];+json.tags[0] = "a";+json.tags[1] = [];+json.tags[1][0] = 1;+json.tags[1][1] = {};+json.meta = {};+json.createdAt = "2026";+json["Content-Type"] = "x";+json["class"] = 1;+json.$v = 2;+json.åke = "ö";+json.escA = "v";+json["1st"] = 0;+`+	out, err := gron([]byte(in))+	if err != nil {+		t.Fatal(err)+	}+	if string(out) != want {+		t.Errorf("got\n%s\nwant\n%s", out, want)+	}+}++func TestGronRejectsBadJSON(t *testing.T) {+	for in, want := range map[string]string{+		"":                                  "line 1, column 1: no value",+		"{\n\"a\" 1}":                       "line 2, column 5: expected ':'",+		`[1 2]`:                             "line 1, column 4: expected ',' or ']'",+		`"a\x"`:                             "line 1, column 4: bad escape",+		`01`:                                "line 1, column 2: data after the value",+		`tru`:                               "line 1, column 1: bad literal",+		`{"a":1,}`:                          "line 1, column 8: expected a string key",+		strings.Repeat("[", gronMaxDepth+1): "nested deeper than",+	} {+		_, err := gron([]byte(in))+		if err == nil || !strings.Contains(err.Error(), want) {+			t.Errorf("%.20q: error %v, want %q", in, err, want)+		}+	}+}++func TestGronView(t *testing.T) {+	s, _, dir := serveTree(t)+	os.WriteFile(filepath.Join(dir, "a.json"), []byte(`{"a":[1]}`), 0o644)+	os.WriteFile(filepath.Join(dir, "bad.json"), []byte(`{"a":}`), 0o644)++	if body := get(s.handler(), "/a.json").Body.String(); !strings.Contains(body, `<a class="left" href="?gron">gron</a>`) {+		t.Errorf("a JSON page has no gron link: %s", body)+	}+	body := get(s.handler(), "/a.json?gron").Body.String()+	if !strings.Contains(body, `<span class="left">gron</span>`) || !strings.Contains(body, `<span id="L3">`) ||+		!strings.Contains(body, `<a href="?">flat</a>`) {+		t.Errorf("no gron view with three numbered lines: %s", body)+	}+	if body := get(s.handler(), "/bad.json?gron").Body.String(); !strings.Contains(body, "not valid JSON: line 1, column 6") ||+		!strings.Contains(body, `<span>flat</span>`) {+		t.Errorf("bad JSON does not say why and show flat: %s", body)+	}+	for _, p := range []string{"/a/b.go", "/a/b.go?gron"} {+		if body := get(s.handler(), p).Body.String(); strings.Contains(body, "gron") {+			t.Errorf("%s offers gron: %s", p, body)+		}+	}+}