docsonnet/pkg/render/render.go

244 lines
5.3 KiB
Go
Raw Normal View History

2020-04-30 00:47:43 +02:00
package render
2020-04-29 16:39:37 +02:00
import (
"encoding/json"
2020-04-29 16:39:37 +02:00
"fmt"
"path"
2020-04-29 16:39:37 +02:00
"sort"
"strings"
"github.com/google/go-jsonnet/formatter"
"github.com/jsonnet-libs/docsonnet/pkg/docsonnet"
"github.com/jsonnet-libs/docsonnet/pkg/md"
"github.com/jsonnet-libs/docsonnet/pkg/slug"
2020-04-29 16:39:37 +02:00
)
type Opts struct {
URLPrefix string
2020-05-02 19:54:34 +02:00
}
func Render(pkg docsonnet.Package, opts Opts) map[string]string {
return render(pkg, nil, true, opts.URLPrefix)
}
func render(pkg docsonnet.Package, parents []string, root bool, urlPrefix string) map[string]string {
link := path.Join("/", urlPrefix, strings.Join(append(parents, pkg.Name), "/"))
2020-05-02 19:54:34 +02:00
if root {
link = path.Join("/", urlPrefix)
2020-05-02 19:54:34 +02:00
}
if !strings.HasSuffix(link, "/") {
link = link + "/"
}
2020-05-02 19:54:34 +02:00
2020-04-29 16:39:37 +02:00
// head
elems := []md.Elem{
2020-05-02 19:54:34 +02:00
md.Frontmatter(map[string]interface{}{
"permalink": link,
}),
2020-04-29 16:39:37 +02:00
md.Headline(1, "package "+pkg.Name),
}
if pkg.Import != "" {
elems = append(elems, md.CodeBlock("jsonnet", fmt.Sprintf(`local %s = import "%s"`, pkg.Name, pkg.Import)))
}
elems = append(elems, md.Text(pkg.Help))
2020-04-29 16:39:37 +02:00
2020-04-30 00:47:43 +02:00
if len(pkg.Sub) > 0 {
elems = append(elems, md.Headline(2, "Subpackages"))
keys := make([]string, 0, len(pkg.Sub))
2020-05-02 19:54:34 +02:00
for _, s := range pkg.Sub {
keys = append(keys, s.Name)
}
sort.Strings(keys)
var items []md.Elem
for _, k := range keys {
s := pkg.Sub[k]
2020-05-02 19:54:34 +02:00
link := strings.Join(append(parents, pkg.Name, s.Name), "-")
if root {
link = strings.Join(append(parents, s.Name), "-")
2020-04-30 00:47:43 +02:00
}
2020-05-02 19:54:34 +02:00
items = append(items, md.Link(md.Text(s.Name), link+".md"))
2020-04-30 00:47:43 +02:00
}
2020-04-30 00:47:43 +02:00
elems = append(elems, md.List(items...))
}
2020-04-29 16:39:37 +02:00
2020-04-30 00:47:43 +02:00
// fields of this package
if len(pkg.API) > 0 {
// index
elems = append(elems,
md.Headline(2, "Index"),
md.List(renderIndex(pkg.API, "", slug.New())...),
)
// api
elems = append(elems, md.Headline(2, "Fields"))
elems = append(elems, renderApi(pkg.API, "")...)
}
2020-04-29 16:39:37 +02:00
2020-05-02 19:54:34 +02:00
content := md.Doc(elems...).String()
key := strings.Join(append(parents, pkg.Name+".md"), "-")
if root {
key = "README.md"
}
out := map[string]string{
key: content,
}
if len(pkg.Sub) != 0 {
for _, s := range pkg.Sub {
path := append(parents, pkg.Name)
if root {
path = parents
}
got := render(s, path, false, urlPrefix)
2020-05-02 19:54:34 +02:00
for k, v := range got {
out[k] = v
}
}
}
return out
2020-04-29 16:39:37 +02:00
}
2020-04-30 00:47:43 +02:00
func renderIndex(api docsonnet.Fields, path string, s *slug.Slugger) []md.Elem {
2020-04-29 16:39:37 +02:00
var elems []md.Elem
for _, k := range sortFields(api) {
v := api[k]
switch {
case v.Function != nil:
fn := v.Function
2020-04-30 00:47:43 +02:00
name := md.Text(fmt.Sprintf("fn %s(%s)", fn.Name, renderParams(fn.Args)))
2020-04-29 16:39:37 +02:00
link := "#" + s.Slug("fn "+path+fn.Name)
elems = append(elems, md.Link(md.Code(name), link))
case v.Object != nil:
obj := v.Object
name := md.Text("obj " + path + obj.Name)
link := "#" + s.Slug("obj "+path+obj.Name)
elems = append(elems, md.Link(md.Code(name), link))
2020-04-30 00:47:43 +02:00
elems = append(elems, md.List(renderIndex(obj.Fields, path+obj.Name+".", s)...))
case v.Value != nil:
val := v.Value
name := md.Text(fmt.Sprintf("%s %s%s", val.Type, path, val.Name))
link := "#" + s.Slug(name.String())
elems = append(elems, md.Link(md.Code(name), link))
2020-04-29 16:39:37 +02:00
}
}
return elems
}
2020-04-30 00:47:43 +02:00
func renderApi(api docsonnet.Fields, path string) []md.Elem {
2020-04-29 16:39:37 +02:00
var elems []md.Elem
for _, k := range sortFields(api) {
v := api[k]
switch {
case v.Function != nil:
fn := v.Function
elems = append(elems,
md.Headline(3, fmt.Sprintf("fn %s%s", path, fn.Name)),
2020-04-30 00:47:43 +02:00
md.CodeBlock("ts", fmt.Sprintf("%s(%s)", fn.Name, renderParams(fn.Args))),
2020-04-29 16:39:37 +02:00
md.Text(fn.Help),
)
case v.Object != nil:
obj := v.Object
elems = append(elems,
md.Headline(2, fmt.Sprintf("obj %s%s", path, obj.Name)),
md.Text(obj.Help),
)
2020-04-30 00:47:43 +02:00
elems = append(elems, renderApi(obj.Fields, path+obj.Name+".")...)
case v.Value != nil:
val := v.Value
elems = append(elems,
md.Headline(3, fmt.Sprintf("%s %s%s", val.Type, path, val.Name)),
)
if val.Default != nil {
elems = append(elems, md.Paragraph(
md.Italic(md.Text("Default value: ")),
md.Code(md.Text(fmt.Sprint(val.Default))),
))
}
elems = append(elems,
md.Text(val.Help),
)
2020-04-29 16:39:37 +02:00
}
}
return elems
}
2020-04-30 00:47:43 +02:00
func sortFields(api docsonnet.Fields) []string {
keys := make([]string, 0, len(api))
2020-04-29 16:39:37 +02:00
for k := range api {
keys = append(keys, k)
}
aFn := func(a, b string) bool {
return api[a].Function != nil && api[b].Function == nil
}
aNew := func(a, b string) bool {
a = strings.ToLower(a)
b = strings.ToLower(b)
return strings.HasPrefix(a, "new") && !strings.HasPrefix(b, "new")
}
2020-04-29 16:39:37 +02:00
sort.Slice(keys, func(i, j int) bool {
a, b := keys[i], keys[j]
if aNew(a, b) {
2020-04-29 16:39:37 +02:00
return true
} else if aNew(b, a) {
return false
2020-04-29 16:39:37 +02:00
}
if aFn(a, b) {
return true
} else if aFn(b, a) {
2020-04-29 16:39:37 +02:00
return false
}
return a < b
2020-04-29 16:39:37 +02:00
})
return keys
}
2020-04-30 00:47:43 +02:00
func renderParams(a []docsonnet.Argument) string {
2020-04-29 16:39:37 +02:00
args := make([]string, 0, len(a))
for _, a := range a {
arg := a.Name
if a.Default != nil {
arg = fmt.Sprintf("%s=%v", arg, jsonParam(a.Default))
2020-04-29 16:39:37 +02:00
}
args = append(args, arg)
}
return strings.Join(args, ", ")
}
func jsonParam(i interface{}) string {
d, err := json.Marshal(i)
if err != nil {
panic(err)
}
s, err := formatter.Format("(jsonParam)", string(d), formatter.Options{
PadObjects: false,
PadArrays: false,
PrettyFieldNames: true,
StringStyle: formatter.StringStyleSingle,
})
if err != nil {
panic(err)
}
return strings.TrimSpace(s)
}