diff --git a/external.go b/external.go index 775ae54..ddc9840 100644 --- a/external.go +++ b/external.go @@ -15,9 +15,9 @@ import ( // limitedWriter wraps a writer and returns an error if the limit is exceeded. type limitedWriter struct { - w io.Writer - n int - limit int + w io.Writer + n int + limit int } func (lw *limitedWriter) Write(p []byte) (int, error) { @@ -34,7 +34,7 @@ type InputMode int const ( InputStdin InputMode = iota // Pass content via stdin (default). - InputTempFile // Write to a temp file, replace {file} in args. + InputTempFile // Write to a temp file, replace {file} and {dir} in args. ) const defaultTimeout = 30 * time.Second @@ -45,7 +45,7 @@ const maxOutputSize = 2 * 1024 * 1024 // ExternalConfig configures an external renderer. type ExternalConfig struct { Command string // Command to execute. - Args []string // Arguments. Use {file} as a placeholder for temp file path. + Args []string // Arguments. {file} is the temp file path, {dir} a per-render scratch directory. InputMode InputMode // How to pass content to the command. Timeout time.Duration // Command timeout. Zero means 30s default. BodyPattern string // Regex to extract body from full HTML output. Empty means use full output. @@ -128,11 +128,16 @@ func (r *ExternalRenderer) renderWithTempFile(content []byte) (string, error) { ext = "." + r.config.Command } - f, err := os.CreateTemp("", "markup-*"+ext) + dir, err := os.MkdirTemp("", "markup-*") + if err != nil { + return "", err + } + defer func() { _ = os.RemoveAll(dir) }() + + f, err := os.CreateTemp(dir, "markup-*"+ext) if err != nil { return "", err } - defer func() { _ = os.Remove(f.Name()) }() if _, err := f.Write(content); err != nil { _ = f.Close() @@ -142,7 +147,8 @@ func (r *ExternalRenderer) renderWithTempFile(content []byte) (string, error) { args := make([]string, len(r.config.Args)) for i, arg := range r.config.Args { - args[i] = strings.ReplaceAll(arg, "{file}", f.Name()) + arg = strings.ReplaceAll(arg, "{file}", f.Name()) + args[i] = strings.ReplaceAll(arg, "{dir}", dir) } ctx, cancel := context.WithTimeout(context.Background(), r.timeout) @@ -224,7 +230,7 @@ func rstRender(content []byte) (string, error) { // toolCache caches which external tools are available. var ( - toolCache sync.Map + toolCache sync.Map ) func toolAvailable(name string) bool { diff --git a/markup.go b/markup.go index b70f1b0..3cd77f8 100644 --- a/markup.go +++ b/markup.go @@ -137,9 +137,9 @@ func NewDefaultRegistry() *Registry { ".rst", ".rest") r.Register(FormatPod, NewExternalRenderer(ExternalConfig{ - Command: "pod2html", - InputMode: InputTempFile, - Args: []string{"--infile={file}", "--quiet"}, + Command: "pod2html", + InputMode: InputTempFile, + Args: []string{"--infile={file}", "--cachedir={dir}", "--quiet"}, BodyPattern: `(?s)
]*>\s*(.*?)\s*`, }), ".pod") diff --git a/markup_test.go b/markup_test.go index ec9ad69..eeab034 100644 --- a/markup_test.go +++ b/markup_test.go @@ -520,6 +520,7 @@ func TestRenderPod(t *testing.T) { t.Skip("pod2html not installed") } content := readTestdata(t, "README.pod") + t.Chdir(t.TempDir()) result, err := r.Render("README.pod", content) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -533,6 +534,13 @@ func TestRenderPod(t *testing.T) { if strings.Contains(result.HTML, "hi")) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if html != "hi
" { + t.Errorf("html = %q, want %q", html, "hi
") + } +} + // --- RendererFunc --- func TestRendererFunc(t *testing.T) {