prettyx

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2025 License: MIT Imports: 9 Imported by: 0

README

prettyx

prettyx formats JSON with deterministic indentation and optional syntax highlighting. It also unwraps JSON values that are themselves encoded as JSON strings, so nested JSON becomes readable without manual decoding (this behaviour is similar to how the jq tool renders JSON with the fromjson builtin).

Install the CLI

go install pkt.systems/prettyx/cmd/prettyx@latest

Run prettyx with one or more JSON files (use - for stdin). Add -no-color to force plain output. Use -no-unwrap to skip decoding JSON appearing inside string values.

By default prettyx unwraps JSON strings recursively so nested documents become objects or arrays. This differs from jq, which leaves embedded JSON as strings unless you explicitly call fromjson (for example: jq '.payload |= fromjson').

prettyx payload.json other.json
cat payload.json | prettyx -no-color
cat payload.json | prettyx -C | less -R

jq equivalent

While not recursive, this example renders identically with jq and prettyx:

$ echo '{"value":"[{"inner": true},{"inner": true,"msg":"true aswell"}]"}' | jq '.value |= fromjson'
{
  "value": [
    {
      "inner": true
    },
    {
      "inner": true,
      "msg": "true aswell"
    }
  ]
}

Use as a library

Import the package and call Pretty (or PrettyWithRenderer if you need custom lipgloss styling).

package main

import (
    "fmt"
    "log"

    "pkt.systems/prettyx"
)

func main() {
    src := []byte(`{"foo":"{\"nested\":true}"}`)
    pretty, err := prettyx.Pretty(src, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Print(string(pretty))
}

Pretty respects the configurable prettyx.MaxNestedJSONDepth, and you can pass custom Options to tweak width, indentation, key sorting, and NoUnwrap when you need the jq-style behaviour of keeping embedded JSON strings untouched.

Designed for recursive jq-style unwrapping

prettyx was designed to replicate the behaviour of the following jq program, which repeatedly calls fromjson on any string that looks like JSON and walks the entire structure:

jq 'def trim($s):
  $s | sub("^\\s+";"") | sub("\\s+$";"");

def looks_like_json($s):
  ($s | length > 1)
  and (
    (($s[0:1] == "{") and ($s[-1:] == "}"))
    or (($s[0:1] == "[") and ($s[-1:] == "]"))
  );

def unwrap:
  if type == "string" then
    (. as $original
     | trim($original) as $trimmed
     | if looks_like_json($trimmed) then
         (try ($trimmed | fromjson | unwrap) catch $original)
       else
         $original
       end)
  elif type == "array" then
    map(unwrap)
  elif type == "object" then
    with_entries(.value |= unwrap)
  else
    .
  end;
unwrap'

Example comparison with sample data:

$ cat sample.json
{"count":2,"message":"ok","payload":"{\"bar\":{\"list\":\"[10,20]\",\"nested\":\"{\\\"inner\\\":true}\"},\"foo\":1}"}

$ jq 'def trim($s):
  $s | sub("^\\s+";"") | sub("\\s+$";"");

def looks_like_json($s):
  ($s | length > 1)
  and (
    (($s[0:1] == "{") and ($s[-1:] == "}"))
    or (($s[0:1] == "[") and ($s[-1:] == "]"))
  );

def unwrap:
  if type == "string" then
    (. as $original
     | trim($original) as $trimmed
     | if looks_like_json($trimmed) then
         (try ($trimmed | fromjson | unwrap) catch $original)
       else
         $original
       end)
  elif type == "array" then
    map(unwrap)
  elif type == "object" then
    with_entries(.value |= unwrap)
  else
    .
  end;
unwrap' sample.json
{
  "count": 2,
  "message": "ok",
  "payload": {
    "bar": {
      "list": [
        10,
        20
      ],
      "nested": {
        "inner": true
      }
    },
    "foo": 1
  }
}

$ prettyx sample.json
{
  "count": 2,
  "message": "ok",
  "payload": {
    "bar": {
      "list": [10, 20],
      "nested": {
        "inner": true
      }
    },
    "foo": 1
  }
}

Documentation

Overview

prettyx builds on the efficient formatting algorithm from Josh Baker's github.com/tidwall/pretty package. We inline the core routines so we can extend them with recursive JSON unwrapping and Lip Gloss styling.

Index

Constants

This section is empty.

Variables

View Source
var DefaultOptions = &Options{Width: 80, Prefix: "", Indent: "  ", SortKeys: false, NoUnwrap: false, ForceColor: false}

DefaultOptions holds the fallback pretty-print configuration.

View Source
var MaxNestedJSONDepth = 10

MaxNestedJSONDepth controls how deep we recursively parse JSON that appears inside string values. Set to 10 by default. Special case:

  • If MaxNestedJSONDepth == 0, we still unwrap one level (i.e., parse the string as JSON once, but do not recurse further).

Example meanings:

0  -> unwrap once (non-recursive)
1  -> unwrap once (same as 0)
2+ -> unwrap up to that many recursive levels

Functions

func NewRenderer added in v0.3.0

func NewRenderer(w io.Writer, forceColor bool) *lipgloss.Renderer

NewRenderer returns a lipgloss renderer configured for the given writer. When forceColor is true, ANSI colors will be emitted even if the writer does not look like a TTY (via termenv's unsafe mode).

func Pretty

func Pretty(in []byte, opts *Options) ([]byte, error)

Pretty parses the input JSON, unwraps nested JSON strings (recursing up to MaxNestedJSONDepth), formats it, and colorizes it with lipgloss before returning the resulting bytes. The renderer automatically adapts to the detected color capabilities of os.Stdout.

func PrettyTo

func PrettyTo(w io.Writer, in []byte, opts *Options, palette *ColorPalette) error

PrettyTo writes a pretty-printed, colorized JSON document to the provided writer using a renderer bound to that writer. Colors degrade automatically when the writer is not a TTY.

func PrettyWithRenderer

func PrettyWithRenderer(in []byte, opts *Options, renderer *lipgloss.Renderer, palette *ColorPalette) ([]byte, error)

PrettyWithRenderer mirrors Pretty but allows callers to provide a custom lipgloss renderer and palette. Passing palette == nil uses the DefaultColorPalette derived from the renderer.

Types

type ColorPalette

type ColorPalette struct {
	Key         lipgloss.Style
	String      lipgloss.Style
	Number      lipgloss.Style
	True        lipgloss.Style
	False       lipgloss.Style
	Null        lipgloss.Style
	Brackets    lipgloss.Style
	Punctuation lipgloss.Style
}

ColorPalette configures the Lip Gloss styles for each JSON token class.

func DefaultColorPalette

func DefaultColorPalette(renderer *lipgloss.Renderer) ColorPalette

DefaultColorPalette returns a VS Code-inspired theme tuned for Lip Gloss. The renderer governs how colors degrade on limited terminals.

func NoColorPalette

func NoColorPalette(renderer *lipgloss.Renderer) ColorPalette

NoColorPalette disables all styling while still routing through lipgloss so we benefit from its rendering decisions (width handling, etc.).

type Options

type Options struct {
	// Width is the max column width for single-line arrays. Default 80.
	Width int
	// Prefix is applied to every output line. Default "".
	Prefix string
	// Indent defines the nested indentation. Default two spaces.
	Indent string
	// SortKeys sorts object keys alphabetically when true. Default false.
	SortKeys bool
	// NoUnwrap disables recursive decoding of JSON strings. This is equivalent to
	// jq's default behaviour (unless you call fromjson) and mirrors the CLI's
	// -no-unwrap flag. When true, prettyx leaves any JSON-looking strings as-is.
	NoUnwrap bool
	// ForceColor forces lipgloss to emit ANSI color even when the destination
	// writer is not detected as a TTY.
	ForceColor bool
}

Options controls the pretty-printing behavior. It mirrors the struct from github.com/tidwall/pretty.

Directories

Path Synopsis
cmd
prettyx command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL