Hugo Custom Output Formats: JSON, AMP, dan Custom Outputs
Hugo Custom Output Formats: JSON, AMP, dan Custom Outputs
Hugo’s output format system adalah fitur powerful yang memungkinkan Anda generate multiple output formats dari content yang sama. Ini membuka kemungkinan untuk membuat JSON APIs, AMP pages, RSS feeds, dan berbagai custom outputs. Panduan ini akan membahas cara memanfaatkan output format system untuk berbagai use cases.
Standard Hugo output adalah HTML, namun banyak aplikasi membutuhkan output formats yang berbeda. Dengan Hugo’s output format system, Anda dapat define custom outputs dan Hugo akan generate semua formats secara otomatis saat build.
Memahami Output Format System
Default Output Formats
Hugo menyediakan beberapa built-in output formats:
HTML adalah format default untuk regular pages. JSON adalah format untuk APIs dan data exchange. RSS adalah format untuk RSS feeds. AMP adalah format untuk Accelerated Mobile Pages.
Mendefinisikan Output Formats
# hugo.toml
[outputs]
home = ["HTML", "JSON", "RSS"]
page = ["HTML", "JSON"]
section = ["HTML", "JSON", "RSS"]
taxonomy = ["HTML", "JSON"]
RSS = ["RSS"]
[mediaTypes]
[mediaTypes.json]
suffix = "json"
type = "application/json"
[mediaTypes.rss]
suffix = "xml"
type = "application/rss+xml"
[mediaTypes.amp]
suffix = "amp.html"
type = "text/html"
[outputFormats]
[outputFormats.json]
mediaType = "mediaTypes.json"
baseName = "index"
isPlainText = true
notAlternative = true
[outputFormats.rss]
mediaType = "mediaTypes.rss"
baseName = "index"
isPlainText = true
[outputFormats.amp]
mediaType = "mediaTypes.amp"
baseName = "index"
path = "amp"
isPlainText = false
Membuat JSON API
JSON untuk Homepage
{{/* layouts/_default/index.json */}}
{{- $.Scratch.Add "data" (dict
"title" .Site.Title
"description" .Site.Params.description
"url" .Permalink
"lastUpdated" .Lastmod
) -}}
{{- $.Scratch.Add "posts" slice -}}
{{- range first 10 (where .Site.RegularPages "Section" "posts") -}}
{{- $.Scratch.Add "posts" (dict
"title" .Title
"slug" .Slug
"date" .Date
"description" .Description
"url" .Permalink
"categories" .Params.categories
"tags" .Params.tags
) -}}
{{- end -}}
{{- $.Scratch.SetInMap "data" "posts" ($.Scratch.Get "posts") -}}
{{- printf "%s" (jsonify ($.Scratch.Get "data")) | resources.FromString "index.json" | minify -}}
JSON untuk Single Post
{{/* layouts/_default/single.json */}}
{{- $post := dict
"title" .Title
"slug" .Slug
"date" .Date
"lastModified" .Lastmod
"description" .Description
"content" (.Content | plainify)
"htmlContent" .Content
"url" .Permalink
"featuredImage" .Params.image
"categories" .Params.categories
"tags" .Params.tags
"author" .Params.author
"readingTime" (div (wordCount .Content) 200 | math.Ceil)
-}}
{{- printf "%s" (jsonify $post) -}}
JSON API untuk Search
{{/* layouts/_default/search.json */}}
{{- $.Scratch.Add "results" slice -}}
{{- range where .Site.RegularPages "Section" "posts" -}}
{{- $.Scratch.Add "results" (dict
"id" .File.UniqueID
"title" .Title
"slug" .Slug
"section" .Section
"date" .Date
"description" .Description
"content" (.Content | plainify | lower)
"url" .Permalink
"categories" .Params.categories
"tags" .Params.tags
) -}}
{{- end -}}
{{- printf "%s" (jsonify (dict "total" ($.Scratch.Get "results" | len) "posts" ($.Scratch.Get "results"))) -}}
JSON Feed untuk Blog
{{/* layouts/_default/feed.json */}}
{{- $title := .Site.Title -}}
{{- $description := .Site.Params.description -}}
{{- $feedUrl := .Permalink -}}
{{- $author := dict
"name" .Site.Params.author
"email" .Site.Params.authorEmail
"url" .Site.BaseURL
-}}
{{- $.Scratch.Add "items" slice -}}
{{- range first 20 (where .Site.RegularPages "Section" "posts") -}}
{{- $.Scratch.Add "items" (dict
"id" .Permalink
"url" .Permalink
"title" .Title
"contentHtml" .Content
"contentText" (.Content | plainify)
"summary" .Description
"image" .Params.image
"datePublished" .Date
"dateModified" .Lastmod
"author" $author
) -}}
{{- end -}}
{{- $feed := dict
"version" "https://jsonfeed.org/version/1"
"title" $title
"home_page_url" .Site.BaseURL
"feed_url" $feedUrl
"description" $description
"author" $author
"items" ($.Scratch.Get "items")
-}}
{{- printf "%s" (jsonify $feed) -}}
Membuat AMP Pages
Base Template untuk AMP
{{/* layouts/_default/baseof.amp */}}
<!doctype html>
<html ⚡ lang="{{ .Site.LanguageCode }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<title>{{ .Title }} | {{ .Site.Title }}</title>
<link rel="canonical" href="{{ .Permalink }}">
{{- with .Description -}}
<meta name="description" content="{{ . }}">
{{- end -}}
<script async src="https://cdn.ampproject.org/v0.js"></script>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<style amp-custom>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1, h2, h3 {
color: #1a1a1a;
line-height: 1.2;
}
img {
max-width: 100%;
height: auto;
}
code {
background: #f4f4f4;
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9em;
}
pre {
background: #f4f4f4;
padding: 15px;
overflow-x: auto;
border-radius: 5px;
}
.meta {
color: #666;
font-size: 0.9em;
}
</style>
{{- template "_internal/opengraph.html" . -}}
</head>
<body>
{{ block "main" . }}{{ end }}
</body>
</html>
Single Post AMP Template
{{/* layouts/_default/single.amp */}}
{{- define "main" -}}
<article>
<header>
<h1>{{ .Title }}</h1>
<p class="meta">
{{ with .Params.author }}{{ . }}{{ end }}
•
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "2 January 2006" }}</time>
</p>
</header>
{{- with .Params.image -}}
<amp-img
src="{{ . }}"
width="800"
height="400"
layout="responsive"
alt="{{ $.Title }}">
</amp-img>
{{- end -}}
<div class="content">
{{ .Content }}
</div>
<footer>
<p class="meta">
{{ with .Params.categories }}
Categories: {{ delimit . ", " }}
{{ end }}
</p>
</footer>
</article>
{{- end -}}
Custom Output Format untuk Sitemap
Extended Sitemap
{{/* layouts/_default/sitemap.xml */}}
{{- printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | safeHTML -}}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
{{- range .Site.RegularPages -}}
<url>
<loc>{{ .Permalink }}</loc>
<lastmod>{{ .Lastmod.Format "2006-01-02" }}</lastmod>
<changefreq>{{ with .Params.changeFreq }}{{ . }}{{ else }}weekly{{ end }}</changefreq>
<priority>{{ with .Params.priority }}{{ . }}{{ else }}{{ cond .IsHome 1.0 0.5 }}{{ end }}</priority>
{{- with .Params.image }}
<image:image>
<image:loc>{{ . }}</image:loc>
<image:title>{{ $.Title }}</image:title>
</image:image>
{{- end -}}
</url>
{{- end -}}
</urlset>
News Sitemap
{{/* layouts/_default/news-sitemap.xml */}}
{{- printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | safeHTML -}}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
{{- range where .Site.RegularPages "Section" "posts" -}}
{{- if ge .Date.AddDate 0 0 -48 -}}
<url>
<loc>{{ .Permalink }}</loc>
<news:news>
<news:publication>
<news:name>{{ .Site.Title }}</news:name>
<news:language>{{ .Site.LanguageCode }}</news:language>
</news:publication>
<news:publication_date>{{ .Date.Format "2006-01-02T15:04:05+00:00" }}</news:publication_date>
<news:title>{{ .Title }}</news:title>
</news:news>
</url>
{{- end -}}
{{- end -}}
</urlset>
Video Sitemap
{{/* layouts/_default/video-sitemap.xml */}}
{{- printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | safeHTML -}}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
{{- range where .Site.RegularPages "Section" "videos" -}}
<url>
<loc>{{ .Permalink }}</loc>
<video:video>
<video:thumbnail_loc>{{ .Params.thumbnail }}</video:thumbnail_loc>
<video:title>{{ .Title }}</video:title>
<video:description>{{ .Description }}</video:description>
<video:player_loc autoplay="false">{{ .Params.videoUrl }}</video:player_loc>
<video:duration>{{ .Params.duration }}</video:duration>
<video:publication_date>{{ .Date.Format "2006-01-02T15:04:05+00:00" }}</video:publication_date>
<video:tag>{{ delimit .Params.tags "," }}</video:tag>
<video:category>{{ .Params.category }}</video:category>
</video:video>
</url>
{{- end -}}
</urlset>
Multiple Output Formats untuk Same Content
Configure Multiple Outputs
# hugo.toml
[outputs]
home = ["HTML", "JSON", "AMP"]
page = ["HTML", "JSON", "AMP"]
[mediaTypes]
[mediaTypes.json]
suffix = "json"
type = "application/json"
[mediaTypes.amp]
suffix = "amp.html"
type = "text/html"
[outputFormats]
[outputFormats.json]
mediaType = "mediaTypes.json"
baseName = "index"
isPlainText = true
notAlternative = true
[outputFormats.amp]
mediaType = "mediaTypes.amp"
baseName = "index"
path = "amp"
isPlainText = false
Link Alternate Outputs di Template
{{/* layouts/_default/baseof.html */}}
<head>
{{- if .IsHome -}}
<link rel="alternate" type="application/json" href="{{ .OutputFormats.Get "JSON" .Permalink }}">
{{- end -}}
{{- range .OutputFormats -}}
{{- if ne .Name "HTML" -}}
<link rel="alternate" type="{{ .MediaType.Type }}" href="{{ .Permalink }}" title="{{ .Name }}">
{{- end -}}
{{- end -}}
</head>
Best Practices
Performance Considerations
Generate multiple outputs menambah build time. Pertimbangkan untuk:
- Use conditional builds dengan
hugo --outputFormats - Cache expensive computations
- Exclude dari production builds jika tidak diperlukan
Template Organization
layouts/
_default/
baseof.html # HTML
baseof.amp # AMP
baseof.json # JSON API
single.html # HTML post
single.amp # AMP post
single.json # JSON post
_internal/
rss.xml # Built-in RSS
Validation
Validate outputs untuk memastikan correctness:
# Validate JSON
cat public/index.json | python -m json.tool > /dev/null && echo "Valid JSON"
# Validate XML
xmllint --noout public/sitemap.xml && echo "Valid XML"
Kesimpulan
Hugo’s output format system adalah fitur powerful yang memungkinkan Anda generate multiple formats dari content yang sama. Dengan memahami dan memanfaatkan fitur ini, Anda dapat membuat APIs, AMP pages, dan custom outputs sesuai kebutuhan proyek.
Artikel Terkait
Link Postingan : https://www.tirinfo.com/hugo-custom-output-formats/