Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c4fb61d

Browse files
authored
commands: Preserve non-content files in convert output
When running `hugo convert` with `--output`, copy the content tree first so non-content bundle resources are kept in the destination, then overwrite converted content files. Also avoid recursive self-copy when the output path points inside the content tree by skipping output directories during copy. Fixes #4621
1 parent d88a29e commit c4fb61d

2 files changed

Lines changed: 77 additions & 3 deletions

File tree

commands/convert.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import (
1818
"context"
1919
"fmt"
2020
"path/filepath"
21+
"slices"
2122
"strings"
2223
"time"
2324

2425
"github.com/bep/simplecobra"
26+
"github.com/gohugoio/hugo/common/hugio"
2527
"github.com/gohugoio/hugo/config"
2628
"github.com/gohugoio/hugo/helpers"
2729
"github.com/gohugoio/hugo/hugofs"
@@ -200,6 +202,56 @@ func (c *convertCommand) convertAndSavePage(p page.Page, site *hugolib.Site, tar
200202
return nil
201203
}
202204

205+
func (c *convertCommand) copyContentDirsForOutput(pagesBackedByFile page.Pages) error {
206+
contentDirs := make(map[string]bool)
207+
for _, p := range pagesBackedByFile {
208+
filename := p.File().Filename()
209+
contentDir := strings.TrimSuffix(filename, p.File().Path())
210+
if contentDir == filename {
211+
continue
212+
}
213+
contentDirs[filepath.Clean(contentDir)] = true
214+
}
215+
216+
var contentDirList []string
217+
for contentDir := range contentDirs {
218+
contentDirList = append(contentDirList, contentDir)
219+
}
220+
slices.Sort(contentDirList)
221+
222+
outputDirAbs, err := filepath.Abs(c.outputDir)
223+
if err != nil {
224+
return fmt.Errorf("failed to resolve output path %q: %w", c.outputDir, err)
225+
}
226+
227+
for _, contentDir := range contentDirList {
228+
outputContentDirAbs := filepath.Join(outputDirAbs, filepath.Base(contentDir))
229+
230+
skipDirs := make(map[string]bool)
231+
relToOutputDir, err := filepath.Rel(contentDir, outputDirAbs)
232+
if err == nil && relToOutputDir != ".." && !strings.HasPrefix(relToOutputDir, ".."+string(filepath.Separator)) {
233+
skipDirs[filepath.Clean(outputDirAbs)] = true
234+
}
235+
relToOutputContentDir, err := filepath.Rel(contentDir, outputContentDirAbs)
236+
if err == nil && relToOutputContentDir != ".." && !strings.HasPrefix(relToOutputContentDir, ".."+string(filepath.Separator)) {
237+
skipDirs[filepath.Clean(outputContentDirAbs)] = true
238+
}
239+
240+
var shouldCopy func(filename string) bool
241+
if len(skipDirs) > 0 {
242+
shouldCopy = func(filename string) bool {
243+
return !skipDirs[filepath.Clean(filename)]
244+
}
245+
}
246+
247+
if err := hugio.CopyDir(hugofs.Os, contentDir, outputContentDirAbs, shouldCopy); err != nil {
248+
return fmt.Errorf("failed to copy %q to %q: %w", contentDir, outputContentDirAbs, err)
249+
}
250+
}
251+
252+
return nil
253+
}
254+
203255
func (c *convertCommand) convertContents(format metadecoders.Format) error {
204256
if c.outputDir == "" && !c.unsafe {
205257
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
@@ -219,6 +271,12 @@ func (c *convertCommand) convertContents(format metadecoders.Format) error {
219271
pagesBackedByFile = append(pagesBackedByFile, p)
220272
}
221273

274+
if c.outputDir != "" {
275+
if err := c.copyContentDirsForOutput(pagesBackedByFile); err != nil {
276+
return err
277+
}
278+
}
279+
222280
site.Log.Println("processing", len(pagesBackedByFile), "content files")
223281
for _, p := range site.AllPages() {
224282
if err := c.convertAndSavePage(p, site, format); err != nil {

testscripts/commands/convert.txt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@ hugo convert toYAML -h
1010
stdout 'to use YAML for the front matter'
1111

1212
hugo convert toJSON -o myjsoncontent
13-
stdout 'processing 3 content files'
13+
stdout 'processing 4 content files'
1414
grep '^{' myjsoncontent/content/mytoml.md
1515
grep '^{' myjsoncontent/content/myjson.md
1616
grep '^{' myjsoncontent/content/myyaml.md
17+
grep '^{' myjsoncontent/content/bundle/index.md
18+
exists myjsoncontent/content/bundle/data.txt
19+
exists myjsoncontent/content/bundle/nested/asset.dat
1720
hugo convert toYAML -o myyamlcontent
18-
stdout 'processing 3 content files'
21+
stdout 'processing 4 content files'
22+
exists myyamlcontent/content/bundle/data.txt
23+
exists myyamlcontent/content/bundle/nested/asset.dat
1924
hugo convert toTOML -o mytomlcontent
20-
stdout 'processing 3 content files'
25+
stdout 'processing 4 content files'
26+
exists mytomlcontent/content/bundle/data.txt
27+
exists mytomlcontent/content/bundle/nested/asset.dat
2128

2229

2330

@@ -40,3 +47,12 @@ JSON content
4047
title: YAML
4148
---
4249
YAML content
50+
-- content/bundle/index.md --
51+
---
52+
title: Bundle
53+
---
54+
Bundle content
55+
-- content/bundle/data.txt --
56+
bundle resource
57+
-- content/bundle/nested/asset.dat --
58+
nested resource

0 commit comments

Comments
 (0)