Hugo — один из самых популярных генераторов статических сайтов. Он написан на языке Go, что дает ему удивительную скорость и гибкость. Тем не менее сейчас Hugo имеет версию v0.79.1, а значит это не финальная его версия и API еще будет меняться. Соответственно, на данный момент, имеются некоторые недоработки. Одна их них - это отсутствие страницы архивов из коробки. Тем не менее, благодаря очень гибкой системе шаблонов и таксономий, эту функциональность очень легко реализовать в теме.

Данный сайт изначально был создан на hexo. Hexo неплохой генератор, но по некоторым причинам мною было принято решение перенести его на hugo. Для hexo существует плагин hexo-generator-archive, который генерирует прикольную страницу со списком всех статей, разбитых по году публикации (пример). И мне очень хотелось оставить эту страницу и на hugo. Причем так, чтобы это не выглядело как костыль.

Как оказалось, это давольно просто, но очень не очевидно. Довольно быстро в официальной документации находится страница про группировку контента. Далее один из примеров почти полностью подходит под наши нужды:

<!-- Groups content by month according to the "date" field in front matter -->
{{ range .Pages.GroupByDate "2006-01" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

Для тех кто не сталкивался с Go: "2006-01" это не просто дата для примера. Go в строке указания формата даты использует конкретное время, а именно: Mon Jan 2 15:04:05 MST 2006. А значит, что данная строка будет аналогом YYYY-MM для большинства других языков.

Итак, теперь осталось только понять, где расположить этот код. Если бы мы хотели создать виджет в боковой колонке, то все было бы просто. Но вот с отдельной страницей нет простых и понятных решений. Самый популярный и рабочий вариант: предлагается все записи добавлять в категорию archive, и использовать /categories/archive в качестве страницы архивов. Данный подход мне очень не нравится, в первую очередь тем, что необходимо вручную добавлять статьи в категорию.

Как оказалось, создать отдельную страницу под какую-то свою логику очень просто. Поможет нам в этом система таксономий.

Первое, что необходимо: добавить новую таксономию в файле конфигурации:

baseURL: "https://devstory.xyz/"
title: DevStory
# ....
taxonomies:
  tag: tags
  archives: archives # тут мы можем указать url нашего архива
# ....

После чего на нашем сайте появиться новая страница по адресу /archives:

Пустая страница /archives

Теперь создадим отдельный шаблон списка archives/list.html для этой таксономии в папке вашей темы.

Зачем вставим код слегка отредактировав html (подробнее об используемых конструкциях можно узнать здесь)

{{ define "main" }}
  <div class="archive">
    {{ range .Pages.GroupByDate "2006" }}
      <h2 class="archive-year">{{ .Key }}</h2>
      {{ range .Pages }}
        <div class="post-item">
          {{ partial "partials/post/post-info-short" . }}
          <a href="{{ .RelPermalink }}" class="post-title-link">{{ .Title }}</a>
        </div>
      {{ end }}
    {{ end }}
  </div>
{{ end }}

Но наша страница не изменится, и все еще будет пуста. Это связано с тем, что в свойстве Pages будет пусто, так как у нас нет страниц, связанных с таксономией archives. А значит, и выведено ничего не будет. Не стоит сразу же бежать и всем записям проставлять archives: true.

Обратившись к документации, мы можем найти два интересных свойства: .Site.Pages и .Site.RegularPages. .Site.Pages - это коллекция всех объектов сайта: страниц, разделов, таксономий и т.д. Его можно использовать, чтобы вывести, скажем, теги и категории, созданные в течение выбранного года. А для фильтрации нужных типов можно использовать функцию where. Но как по мне, более логично использовать записи из свойства .Site.RegularPages, в котором уже заранее отфильтрованы только страницы (нужно обратить внимание, что будут включены все страницы из папки content, а не только записи из папки content/posts). И так, итоговый код будет выглядеть вот так:

{{ define "main" }}
  <div class="archive">
    {{ range .Site.RegularPages.GroupByDate "2006" }}
      <h2 class="archive-year">{{ .Key }}</h2>
      {{ range .Pages }}
        <div class="post-item">
          {{ partial "partials/post/post-info-short" . }}
          <a href="{{ .RelPermalink }}" class="post-title-link">{{ .Title }}</a>
        </div>
      {{ end }}
    {{ end }}
  </div>
{{ end }}

И мы увидим все статьи сайта, именно, так как мы хотели:

Итоговая страница архивов

Если мы сгенерируем сайт и посмотрим содержимое папки public/archives, то увидим там только файлы index.html и index.xml.

Итоговая файловая структура папки archives

Так как нет записей с таксономией archives, hugo сгенерирует только “пустой” список страниц и больше ничего. Это означает, что на сайте не будет случайных “мусорных” страниц. А в index.html будет содержаться сгенерированный нами список страниц, разбитый по годом, без пагинации.