Repository: gohugoio/hugo (language: go)

Issue:
Page resources missing when language variant is draft
When you have different language variants of a page (say `example/index.md` and `example/index.de.md`) and one of them is a draft, `.Resources.Get` and `.Resources.GetMatch` return `nil` for any resources.

In Hugo 0.122.0 it worked fine. Here's a minimal, reproducible example. Just unpack it and run `hugo`.
[quickstart.zip](https://github.com/gohugoio/hugo/files/14363853/quickstart.zip)

### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.123.1-3f8434a62fc91a50f4e5bd43a4435d2d301844ff+extended darwin/arm64 BuildDate=2024-02-21T08:17:45Z VendorInfo=brew
</pre>

### Does this issue reproduce with the latest release?

Yes.


Discussion / hints:
I will test this, but one of the changes in v0.123.0 is that we don't duplicate resources (except in some cases) per language, so _one resource file_ is attached to one language. That also means that if you draft the owning header, that resource also goes away. Does this match what you're seeing?
That sounds good. But when I draft `example/index.de.md` then `cover.jpg` goes missing for `example/index.md`. So I cannot draft translations of published articles without breaking my build.
I can confirm the problem. Drafting a non-default language page removes the shared resource for all translations.

```text
git clone --single-branch -b hugo-github-issue-12105 https://github.com/jmooring/hugo-testing hugo-github-issue-12105
cd hugo-github-issue-12105
rm -rf public/ && hugo && tree public
```

Content:

```text
content/
├── de/
│   ├── my-bundle/
│   │   └── index.md   <-- draft = true
│   └── _index.md
├── en/                <-- this is the default content language
│   ├── my-bundle/
│   │   ├── a.jpg
│   │   └── index.md
│   └── _index.md
├── fr/
│   ├── my-bundle/
│   │   └── index.md
│   └── _index.md
└── _index.md

```

Expected output:

```text
public/
├── de/
│   └── index.html
├── en/
│   ├── my-bundle/
│   │   ├── a.jpg
│   │   └── index.html
│   └── index.html
├── fr/
│   ├── my-bundle/
│   │   └── index.html
│   └── index.html
├── favicon.ico
└── index.html
```

Actual output:

```text
public/
├── de/
│   └── index.html
├── en/
│   ├── my-bundle/
│   │   └── index.html
│   └── index.html
├── fr/
│   ├── my-bundle/
│   │   └── index.html
│   └── index.html
├── favicon.ico
└── index.html
```

Fix patch:
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index 5f0b11210ca..ee7f2267615 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -221,10 +221,22 @@ func (t pageTrees) Shape(d, v int) *pageTrees {
 	t.treePages = t.treePages.Shape(d, v)
 	t.treeResources = t.treeResources.Shape(d, v)
 	t.treeTaxonomyEntries = t.treeTaxonomyEntries.Shape(d, v)
+	t.createMutableTrees()
 
 	return &t
 }
 
+func (t *pageTrees) createMutableTrees() {
+	t.treePagesResources = doctree.WalkableTrees[contentNodeI]{
+		t.treePages,
+		t.treeResources,
+	}
+
+	t.resourceTrees = doctree.MutableTrees{
+		t.treeResources,
+	}
+}
+
 var (
 	_ resource.Identifier = pageMapQueryPagesInSection{}
 	_ resource.Identifier = pageMapQueryPagesBelowPath{}
@@ -676,9 +688,15 @@ func (s *contentNodeShifter) Delete(n contentNodeI, dimension doctree.Dimension)
 		}
 		return wasDeleted, isEmpty
 	case *resourceSource:
+		if lidx > 0 {
+			return false, false
+		}
 		resource.MarkStale(v)
 		return true, true
 	case *pageState:
+		if lidx > 0 {
+			return false, false
+		}
 		resource.MarkStale(v)
 		return true, true
 	default:
@@ -1694,6 +1712,9 @@ func (sa *sitePagesAssembler) removeShouldNotBuild() error {
 		return err
 	}
 
+	if len(keys) == 0 {
+		return nil
+	}
 	sa.pageMap.DeletePageAndResourcesBelow(keys...)
 
 	return nil
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
index debb81e77b1..2e893349799 100644
--- a/hugolib/site_new.go
+++ b/hugolib/site_new.go
@@ -165,14 +165,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
 		treeTaxonomyEntries: doctree.NewTreeShiftTree[*weightedContentNode](doctree.DimensionLanguage.Index(), len(confm.Languages)),
 	}
 
-	pageTrees.treePagesResources = doctree.WalkableTrees[contentNodeI]{
-		pageTrees.treePages,
-		pageTrees.treeResources,
-	}
-
-	pageTrees.resourceTrees = doctree.MutableTrees{
-		pageTrees.treeResources,
-	}
+	pageTrees.createMutableTrees()
 
 	for i, confp := range confm.ConfigLangs() {
 		language := confp.Language()


Test patch:
diff --git a/hugolib/pagebundler_test.go b/hugolib/pagebundler_test.go
index e9686706d0f..ff7998ed933 100644
--- a/hugolib/pagebundler_test.go
+++ b/hugolib/pagebundler_test.go
@@ -893,3 +893,29 @@ Match: {{ range .Resources.Match "f1.en.*" }}{{ .Name }}: {{ .Content }}|{{ end
 
 	b.AssertFileContent("public/mybundle/index.html", "GetMatch: f1.txt: F1.|", "Match: f1.txt: F1.|")
 }
+
+func TestBundleResourcesWhenLanguageVariantIsDraft(t *testing.T) {
+	files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+defaultContentLanguage = "en"
+[languages]
+[languages.en]
+weight = 1
+[languages.nn]
+weight = 2
+-- content/mybundle/index.en.md --
+-- content/mybundle/index.nn.md --
+---
+draft: true
+---
+-- content/mybundle/f1.en.txt --
+F1.
+-- layouts/_default/single.html --
+GetMatch: {{ with .Resources.GetMatch "f1.*" }}{{ .Name }}: {{ .Content }}|{{ end }}$
+`
+
+	b := Test(t, files)
+
+	b.AssertFileContent("public/mybundle/index.html", "GetMatch: f1.txt: F1.|")
+}