Skip to content

Commit

Permalink
Merge pull request #1 from urfave/extern-templates
Browse files Browse the repository at this point in the history
Move templates from string literals to embedded strings
  • Loading branch information
meatballhat committed Jun 26, 2023
2 parents 18f0005 + 628d08a commit 07c2cad
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 162 deletions.
1 change: 1 addition & 0 deletions .gitattributes
@@ -0,0 +1 @@
*.gotmpl text eol=lf
9 changes: 8 additions & 1 deletion docs.go
Expand Up @@ -2,6 +2,7 @@ package docs

import (
"bytes"
_ "embed"
"fmt"
"io"
"os"
Expand All @@ -18,6 +19,12 @@ import (
)

var (
//go:embed markdown.md.gotmpl
MarkdownDocTemplate string

//go:embed markdown_tabular.md.gotmpl
MarkdownTabularDocTemplate string

isTracingOn = os.Getenv("URFAVE_CLI_TRACING") == "on"
)

Expand Down Expand Up @@ -114,7 +121,7 @@ func ToTabularToFileBetweenTags(cmd *cli.Command, appPath, filePath string, star
return err
}

const comment = "<!-- Documentation inside this block generated by github.com/urfave/cli; DO NOT EDIT -->"
const comment = "<!-- Documentation inside this block generated by github.com/urfave/cli-docs/v3; DO NOT EDIT -->"

// replace content between start and end tags
updated := re.ReplaceAll(content, []byte(strings.Join([]string{start, comment, md, end}, "\n")))
Expand Down
109 changes: 54 additions & 55 deletions docs_test.go
Expand Up @@ -2,6 +2,7 @@ package docs

import (
"bytes"
"embed"
"io"
"io/fs"
"net/mail"
Expand All @@ -12,14 +13,30 @@ import (
"github.com/urfave/cli/v3"
)

var (
//go:embed testdata
testdata embed.FS
)

func expectFileContent(t *testing.T, file, got string) {
data, err := os.ReadFile(file)
// Ignore windows line endings
data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n"))
data, err := testdata.ReadFile(file)

r := require.New(t)
r.NoError(err)
r.Equal(got, string(data))
r.Equal(
string(normalizeNewlines([]byte(got))),
string(normalizeNewlines(data)),
)
}

func normalizeNewlines(d []byte) []byte {
return bytes.ReplaceAll(
bytes.ReplaceAll(
d,
[]byte("\r\n"), []byte("\n"),
),
[]byte("\r"), []byte("\n"),
)
}

func buildExtendedTestCommand() *cli.Command {
Expand Down Expand Up @@ -137,13 +154,8 @@ Should be a part of the same code block
}

func TestToMarkdownFull(t *testing.T) {
// Given
cmd := buildExtendedTestCommand()

// When
res, err := ToMarkdown(cmd)

// Then
require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-full.md", res)
}
Expand All @@ -152,28 +164,19 @@ func TestToTabularMarkdown(t *testing.T) {
app := buildExtendedTestCommand()

t.Run("full", func(t *testing.T) {
// When
res, err := ToTabularMarkdown(app, "app")

// Then
require.NoError(t, err)
expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res)
})

t.Run("with empty path", func(t *testing.T) {
// When
res, err := ToTabularMarkdown(app, "")

// Then
require.NoError(t, err)
expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res)
})

t.Run("with custom app path", func(t *testing.T) {
// When
res, err := ToTabularMarkdown(app, "/usr/local/bin")

// Then
require.NoError(t, err)
expectFileContent(t, "testdata/expected-tabular-markdown-custom-app-path.md", res)
})
Expand All @@ -183,7 +186,7 @@ func TestToTabularMarkdownFailed(t *testing.T) {
tpl := MarkdownTabularDocTemplate
t.Cleanup(func() { MarkdownTabularDocTemplate = tpl })

MarkdownTabularDocTemplate = "{{ .Foo }}" // invalid template
MarkdownTabularDocTemplate = "{{ .Foo }}"

app := buildExtendedTestCommand()

Expand All @@ -196,17 +199,11 @@ func TestToTabularMarkdownFailed(t *testing.T) {
}

func TestToTabularToFileBetweenTags(t *testing.T) {
expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md")
expectedDocs, fErr := testdata.ReadFile("testdata/expected-tabular-markdown-full.md")

r := require.New(t)
r.NoError(fErr)

// normalizes \r\n (windows) and \r (mac) into \n (unix) (required for tests to pass on windows)
normalizeNewlines := func(d []byte) []byte {
d = bytes.ReplaceAll(d, []byte{13, 10}, []byte{10}) // replace CR LF \r\n (windows) with LF \n (unix)
return bytes.ReplaceAll(d, []byte{13}, []byte{10}) // replace CF \r (mac) with LF \n (unix)
}

t.Run("default tags", func(t *testing.T) {
tmpFile, err := os.CreateTemp("", "")

Expand Down Expand Up @@ -238,7 +235,7 @@ Some other text`)
Some description
<!--GENERATED:CLI_DOCS-->
<!-- Documentation inside this block generated by github.com/urfave/cli; DO NOT EDIT -->
<!-- Documentation inside this block generated by github.com/urfave/cli-docs/v3; DO NOT EDIT -->
` + string(expectedDocs) + `
<!--/GENERATED:CLI_DOCS-->
Expand Down Expand Up @@ -278,7 +275,7 @@ Some other text`)
Some description
foo_BAR|baz
<!-- Documentation inside this block generated by github.com/urfave/cli; DO NOT EDIT -->
<!-- Documentation inside this block generated by github.com/urfave/cli-docs/v3; DO NOT EDIT -->
` + string(expectedDocs) + `
lorem+ipsum
Expand All @@ -302,44 +299,46 @@ Some other text`))
})
}

func TestToMarkdownNoFlags(t *testing.T) {
app := buildExtendedTestCommand()
app.Flags = nil
func TestToMarkdown(t *testing.T) {
t.Run("no flags", func(t *testing.T) {
app := buildExtendedTestCommand()
app.Flags = nil

res, err := ToMarkdown(app)
res, err := ToMarkdown(app)

require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-flags.md", res)
}
require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-flags.md", res)
})

func TestToMarkdownNoCommands(t *testing.T) {
app := buildExtendedTestCommand()
app.Commands = nil
t.Run("no commands", func(t *testing.T) {
app := buildExtendedTestCommand()
app.Commands = nil

res, err := ToMarkdown(app)
res, err := ToMarkdown(app)

require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-commands.md", res)
}
require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-commands.md", res)
})

func TestToMarkdownNoAuthors(t *testing.T) {
app := buildExtendedTestCommand()
app.Authors = []any{}
t.Run("no authors", func(t *testing.T) {
app := buildExtendedTestCommand()
app.Authors = []any{}

res, err := ToMarkdown(app)
res, err := ToMarkdown(app)

require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-authors.md", res)
}
require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-authors.md", res)
})

func TestToMarkdownNoUsageText(t *testing.T) {
app := buildExtendedTestCommand()
app.UsageText = ""
t.Run("no usage text", func(t *testing.T) {
app := buildExtendedTestCommand()
app.UsageText = ""

res, err := ToMarkdown(app)
res, err := ToMarkdown(app)

require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-usagetext.md", res)
require.NoError(t, err)
expectFileContent(t, "testdata/expected-doc-no-usagetext.md", res)
})
}

func TestToMan(t *testing.T) {
Expand Down
32 changes: 32 additions & 0 deletions markdown.md.gotmpl
@@ -0,0 +1,32 @@
{{if gt .SectionNum 0}}% {{ .Command.Name }} {{ .SectionNum }}

{{end}}# NAME

{{ .Command.Name }}{{ if .Command.Usage }} - {{ .Command.Usage }}{{ end }}

# SYNOPSIS

{{ .Command.Name }}
{{ if .SynopsisArgs }}
```
{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}```
{{ end }}{{ if .Command.Description }}
# DESCRIPTION

{{ .Command.Description }}
{{ end }}
**Usage**:

```{{ if .Command.UsageText }}
{{ .Command.UsageText }}
{{ else }}
{{ .Command.Name }} [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...]
{{ end }}```
{{ if .GlobalArgs }}
# GLOBAL OPTIONS
{{ range $v := .GlobalArgs }}
{{ $v }}{{ end }}
{{ end }}{{ if .Commands }}
# COMMANDS
{{ range $v := .Commands }}
{{ $v }}{{ end }}{{ end -}}
71 changes: 71 additions & 0 deletions markdown_tabular.md.gotmpl
@@ -0,0 +1,71 @@
{{ define "flags" }}
| Name | Description | Default value | Environment variables |
|------|-------------|:-------------:|:---------------------:|
{{ range $flag := . -}}
{{- /**/ -}} | `{{ $flag.Name }}{{ if $flag.TakesValue }}="…"{{ end }}` {{ if $flag.Aliases }}(`{{ join $flag.Aliases "`, `" }}`) {{ end }}
{{- /**/ -}} | {{ $flag.Usage }}
{{- /**/ -}} | {{ if $flag.Default }}`{{ $flag.Default }}`{{ end }}
{{- /**/ -}} | {{ if $flag.EnvVars }}`{{ join $flag.EnvVars "`, `" }}`{{ else }}*none*{{ end }}
{{- /**/ -}} |
{{ end }}
{{ end }}

{{ define "command" }}
### `{{ .Name }}` {{ if gt .Level 0 }}sub{{ end }}command{{ if .Aliases }} (aliases: `{{ join .Aliases "`, `" }}`){{ end }}
{{ if .Usage }}
{{ .Usage }}.
{{ end }}
{{ if .UsageText }}
{{ range $line := .UsageText -}}
> {{ $line }}
{{ end -}}
{{ end }}
{{ if .Description }}
{{ .Description }}.
{{ end }}
Usage:

```bash
$ {{ .AppPath }} [GLOBAL FLAGS] {{ .Name }}{{ if .Flags }} [COMMAND FLAGS]{{ end }} {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }}
```

{{ if .Flags -}}
The following flags are supported:
{{ template "flags" .Flags }}
{{ end -}}

{{ if .SubCommands -}}
{{ range $subCmd := .SubCommands -}}
{{ template "command" $subCmd }}
{{ end -}}
{{ end -}}
{{ end }}

## CLI interface{{ if .Name }} - {{ .Name }}{{ end }}

{{ if .Description }}{{ .Description }}.
{{ end }}
{{ if .Usage }}{{ .Usage }}.
{{ end }}
{{ if .UsageText }}
{{ range $line := .UsageText -}}
> {{ $line }}
{{ end -}}
{{ end }}
Usage:

```bash
$ {{ .AppPath }}{{ if .GlobalFlags }} [GLOBAL FLAGS]{{ end }} [COMMAND] [COMMAND FLAGS] {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }}
```

{{ if .GlobalFlags }}
Global flags:

{{ template "flags" .GlobalFlags }}

{{ end -}}
{{ if .Commands -}}
{{ range $cmd := .Commands -}}
{{ template "command" $cmd }}
{{ end }}
{{- end }}

0 comments on commit 07c2cad

Please sign in to comment.