Skip to content

Commit a98204d

Browse files
authoredJul 8, 2024··
feat: add templ info command (#840)
1 parent 13f712b commit a98204d

File tree

8 files changed

+243
-12
lines changed

8 files changed

+243
-12
lines changed
 

‎.github/ISSUE_TEMPLATE/bug_report.md

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ If applicable, add screenshots or screen captures to help explain your problem.
2525
**Logs**
2626
If the issue is related to IDE support, run through the LSP troubleshooting section at https://templ.guide/commands-and-tools/ide-support/#troubleshooting-1 and include logs from templ
2727

28+
**`templ info` output**
29+
Run `templ info` and include the output.
30+
2831
**Desktop (please complete the following information):**
2932
- OS: [e.g. MacOS, Linux, Windows, WSL]
3033
- templ CLI version (`templ version`)

‎.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.747
1+
0.2.749

‎cmd/templ/infocmd/main.go

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package infocmd
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"log/slog"
10+
"os"
11+
"os/exec"
12+
"runtime"
13+
"strings"
14+
15+
"github.com/a-h/templ"
16+
"github.com/a-h/templ/cmd/templ/lspcmd/pls"
17+
)
18+
19+
type Arguments struct {
20+
JSON bool `flag:"json" help:"Output info as JSON."`
21+
}
22+
23+
type Info struct {
24+
OS struct {
25+
GOOS string `json:"goos"`
26+
GOARCH string `json:"goarch"`
27+
} `json:"os"`
28+
Go ToolInfo `json:"go"`
29+
Gopls ToolInfo `json:"gopls"`
30+
Templ ToolInfo `json:"templ"`
31+
}
32+
33+
type ToolInfo struct {
34+
Location string `json:"location"`
35+
Version string `json:"version"`
36+
OK bool `json:"ok"`
37+
Message string `json:"message,omitempty"`
38+
}
39+
40+
func getGoInfo() (d ToolInfo) {
41+
// Find Go.
42+
var err error
43+
d.Location, err = exec.LookPath("go")
44+
if err != nil {
45+
d.Message = fmt.Sprintf("failed to find go: %v", err)
46+
return
47+
}
48+
// Run go to find the version.
49+
cmd := exec.Command(d.Location, "version")
50+
v, err := cmd.Output()
51+
if err != nil {
52+
d.Message = fmt.Sprintf("failed to get go version, check that Go is installed: %v", err)
53+
return
54+
}
55+
d.Version = strings.TrimSpace(string(v))
56+
d.OK = true
57+
return
58+
}
59+
60+
func getGoplsInfo() (d ToolInfo) {
61+
var err error
62+
d.Location, err = pls.FindGopls()
63+
if err != nil {
64+
d.Message = fmt.Sprintf("failed to find gopls: %v", err)
65+
return
66+
}
67+
cmd := exec.Command(d.Location, "version")
68+
v, err := cmd.Output()
69+
if err != nil {
70+
d.Message = fmt.Sprintf("failed to get gopls version: %v", err)
71+
return
72+
}
73+
d.Version = strings.TrimSpace(string(v))
74+
d.OK = true
75+
return
76+
}
77+
78+
func getTemplInfo() (d ToolInfo) {
79+
// Find templ.
80+
var err error
81+
d.Location, err = findTempl()
82+
if err != nil {
83+
d.Message = err.Error()
84+
return
85+
}
86+
// Run templ to find the version.
87+
cmd := exec.Command(d.Location, "version")
88+
v, err := cmd.Output()
89+
if err != nil {
90+
d.Message = fmt.Sprintf("failed to get templ version: %v", err)
91+
return
92+
}
93+
d.Version = strings.TrimSpace(string(v))
94+
if d.Version != templ.Version() {
95+
d.Message = fmt.Sprintf("version mismatch - you're running %q at the command line, but the version in the path is %q", templ.Version(), d.Version)
96+
return
97+
}
98+
d.OK = true
99+
return
100+
}
101+
102+
func findTempl() (location string, err error) {
103+
executableName := "templ"
104+
if runtime.GOOS == "windows" {
105+
executableName = "templ.exe"
106+
}
107+
executableName, err = exec.LookPath(executableName)
108+
if err == nil {
109+
// Found on the path.
110+
return executableName, nil
111+
}
112+
113+
// Unexpected error.
114+
if !errors.Is(err, exec.ErrNotFound) {
115+
return "", fmt.Errorf("unexpected error looking for templ: %w", err)
116+
}
117+
118+
return "", fmt.Errorf("templ is not in the path (%q). You can install templ with `go install github.com/a-h/templ/cmd/templ@latest`", os.Getenv("PATH"))
119+
}
120+
121+
func getInfo() (d Info) {
122+
d.OS.GOOS = runtime.GOOS
123+
d.OS.GOARCH = runtime.GOARCH
124+
d.Go = getGoInfo()
125+
d.Gopls = getGoplsInfo()
126+
d.Templ = getTemplInfo()
127+
return
128+
}
129+
130+
func Run(ctx context.Context, log *slog.Logger, stdout io.Writer, args Arguments) (err error) {
131+
info := getInfo()
132+
if args.JSON {
133+
enc := json.NewEncoder(stdout)
134+
enc.SetIndent("", " ")
135+
return enc.Encode(info)
136+
}
137+
log.Info("os", slog.String("goos", info.OS.GOOS), slog.String("goarch", info.OS.GOARCH))
138+
logInfo(ctx, log, "go", info.Go)
139+
logInfo(ctx, log, "gopls", info.Gopls)
140+
logInfo(ctx, log, "templ", info.Templ)
141+
return nil
142+
}
143+
144+
func logInfo(ctx context.Context, log *slog.Logger, name string, ti ToolInfo) {
145+
level := slog.LevelInfo
146+
if !ti.OK {
147+
level = slog.LevelError
148+
}
149+
args := []any{
150+
slog.String("location", ti.Location),
151+
slog.String("version", ti.Version),
152+
}
153+
if ti.Message != "" {
154+
args = append(args, slog.String("message", ti.Message))
155+
}
156+
log.Log(ctx, level, name, args...)
157+
}

‎cmd/templ/lspcmd/pls/main.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ func (opts Options) AsArguments() []string {
3131
return args
3232
}
3333

34-
func findGopls() (location string, err error) {
34+
func FindGopls() (location string, err error) {
3535
executableName := "gopls"
3636
if runtime.GOOS == "windows" {
3737
executableName = "gopls.exe"
3838
}
3939

40-
_, err = exec.LookPath(executableName)
40+
executableName, err = exec.LookPath(executableName)
4141
if err == nil {
4242
// Found on the path.
4343
return executableName, nil
@@ -72,7 +72,7 @@ func findGopls() (location string, err error) {
7272

7373
// NewGopls starts gopls and opens up a jsonrpc2 connection to it.
7474
func NewGopls(ctx context.Context, log *zap.Logger, opts Options) (rwc io.ReadWriteCloser, err error) {
75-
location, err := findGopls()
75+
location, err := FindGopls()
7676
if err != nil {
7777
return nil, err
7878
}

‎cmd/templ/main.go

+57
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/a-h/templ"
1414
"github.com/a-h/templ/cmd/templ/fmtcmd"
1515
"github.com/a-h/templ/cmd/templ/generatecmd"
16+
"github.com/a-h/templ/cmd/templ/infocmd"
1617
"github.com/a-h/templ/cmd/templ/lspcmd"
1718
"github.com/a-h/templ/cmd/templ/sloghandler"
1819
"github.com/fatih/color"
@@ -35,6 +36,7 @@ commands:
3536
generate Generates Go code from templ files
3637
fmt Formats templ files
3738
lsp Starts a language server for templ files
39+
info Displays information about the templ environment
3840
version Prints the version
3941
`
4042

@@ -44,6 +46,8 @@ func run(stdin io.Reader, stdout, stderr io.Writer, args []string) (code int) {
4446
return 64 // EX_USAGE
4547
}
4648
switch args[1] {
49+
case "info":
50+
return infoCmd(stdout, stderr, args[2:])
4751
case "generate":
4852
return generateCmd(stdout, stderr, args[2:])
4953
case "fmt":
@@ -80,6 +84,59 @@ func newLogger(logLevel string, verbose bool, stderr io.Writer) *slog.Logger {
8084
}))
8185
}
8286

87+
const infoUsageText = `usage: templ info [<args>...]
88+
89+
Displays information about the templ environment.
90+
91+
Args:
92+
-json
93+
Output information in JSON format to stdout. (default false)
94+
-v
95+
Set log verbosity level to "debug". (default "info")
96+
-log-level
97+
Set log verbosity level. (default "info", options: "debug", "info", "warn", "error")
98+
-help
99+
Print help and exit.
100+
`
101+
102+
func infoCmd(stdout, stderr io.Writer, args []string) (code int) {
103+
cmd := flag.NewFlagSet("diagnose", flag.ExitOnError)
104+
jsonFlag := cmd.Bool("json", false, "")
105+
verboseFlag := cmd.Bool("v", false, "")
106+
logLevelFlag := cmd.String("log-level", "info", "")
107+
helpFlag := cmd.Bool("help", false, "")
108+
err := cmd.Parse(args)
109+
if err != nil {
110+
fmt.Fprint(stderr, infoUsageText)
111+
return 64 // EX_USAGE
112+
}
113+
if *helpFlag {
114+
fmt.Fprint(stdout, infoUsageText)
115+
return
116+
}
117+
118+
log := newLogger(*logLevelFlag, *verboseFlag, stderr)
119+
120+
ctx, cancel := context.WithCancel(context.Background())
121+
signalChan := make(chan os.Signal, 1)
122+
signal.Notify(signalChan, os.Interrupt)
123+
go func() {
124+
<-signalChan
125+
fmt.Fprintln(stderr, "Stopping...")
126+
cancel()
127+
}()
128+
129+
err = infocmd.Run(ctx, log, stdout, infocmd.Arguments{
130+
JSON: *jsonFlag,
131+
})
132+
if err != nil {
133+
color.New(color.FgRed).Fprint(stderr, "(✗) ")
134+
fmt.Fprintln(stderr, "Command failed: "+err.Error())
135+
return 1
136+
}
137+
return 0
138+
}
139+
83140
const generateUsageText = `usage: templ generate [<args>...]
84141
85142
Generates Go code from templ files.

‎cmd/templ/main_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ func TestMain(t *testing.T) {
6565
expectedStdout: lspUsageText,
6666
expectedCode: 0,
6767
},
68+
{
69+
name: `"templ info --help" prints usage`,
70+
args: []string{"templ", "info", "--help"},
71+
expectedStdout: infoUsageText,
72+
expectedCode: 0,
73+
},
6874
}
6975

7076
for _, test := range tests {

‎docs/docs/09-commands-and-tools/01-cli.md

+12-8
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
`templ` provides a command line interface. Most users will only need to run the `templ generate` command to generate Go code from `*.templ` files.
44

55
```
6-
usage: templ <command> [parameters]
7-
To see help text, you can run:
8-
templ generate --help
9-
templ fmt --help
10-
templ lsp --help
11-
templ version
12-
examples:
13-
templ generate
6+
usage: templ <command> [<args>...]
7+
8+
templ - build HTML UIs with Go
9+
10+
See docs at https://templ.guide
11+
12+
commands:
13+
generate Generates Go code from templ files
14+
fmt Formats templ files
15+
lsp Starts a language server for templ files
16+
info Displays information about the templ environment
17+
version Prints the version
1418
```
1519

1620
## Generating Go code from templ files

‎docs/docs/09-commands-and-tools/02-ide-support.md

+4
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,7 @@ The logs can be quite verbose, since almost every keypress results in additional
433433
### Look at the web server
434434

435435
The web server option provides an insight into the internal state of the language server. It may provide insight into what's going wrong.
436+
437+
### Run templ info
438+
439+
The `templ info` command outputs information that's useful for debugging issues.

0 commit comments

Comments
 (0)
Please sign in to comment.