Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions external.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions markup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)<body[^>]*>\s*(.*?)\s*</body>`,
}), ".pod")

Expand Down
25 changes: 25 additions & 0 deletions markup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -533,6 +534,13 @@ func TestRenderPod(t *testing.T) {
if strings.Contains(result.HTML, "<html") {
t.Errorf("expected body-only HTML, got full document")
}
entries, err := os.ReadDir(".")
if err != nil {
t.Fatal(err)
}
if len(entries) != 0 {
t.Errorf("renderer created files: %v", entries)
}
}

func TestRenderTextile(t *testing.T) {
Expand Down Expand Up @@ -719,6 +727,23 @@ func TestExternalRendererBodyExtraction(t *testing.T) {
}
}

func TestExternalRendererTempFilePlaceholders(t *testing.T) {
r := NewExternalRenderer(ExternalConfig{
Command: "sh",
Args: []string{"-c", `test "$(dirname "$2")" = "$1" && cat "$2"`, "sh", "{dir}", "{file}"},
InputMode: InputTempFile,
TempExt: ".txt",
})

html, err := r.Render([]byte("<p>hi</p>"))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if html != "<p>hi</p>" {
t.Errorf("html = %q, want %q", html, "<p>hi</p>")
}
}

// --- RendererFunc ---

func TestRendererFunc(t *testing.T) {
Expand Down
Loading