• SIS Lab
    • >
    • Blog
    • >
    • HugoでAMP対応のブログカードを作る

HugoでAMP対応のブログカードを作る

更新日:2022.08.31 作成日:2020.02.02

「Hugoでもブログカードを利用したい」

そう考えているところに以下の記事がTwitterのTLで流れてきたので、試してみました。

Hugoでブログカードに対応する | Hugo 入門 / 解説 | nasust dev blog

ブログカードのShortcodeと表示例

{{% blogcard https://nasust.com/hugo/shortcode/blogcard/ %}}

blogcardというShortcodeを利用することで、対象ページのTitle, Description, OGPを取得して表示します。

Shortcodeblogcardの実装は以下の通りです。ローカルに立ち上げたAPIサーバ(Express)に対してgetJSONを行うことで、ビルド時に情報を取得します。

Shortcode for blogcard

{{ $url := ( .Get 0) }}
{{ $getURL := printf "http://localhost:6060/getogp?%s" (querify "url" $url ) }}
{{ $ogpjson := getJSON $getURL }}
{{ $getSizeURL := printf "http://localhost:6060/size?%s" (querify "url" $ogpjson.image ) }}
{{ $imgjson := getJSON $getSizeURL }}

<p></p><!-- Avoid overriding list -->
<div class="embed">
    <div class="rf">
        <div class="rr">
            <div class="rcx9 rcm9 rpls">
                <div class="rf">
                    <div class="rr">
                        <div class="rcx12 body">
                            <a class="utdn"
                                href="{{ $ogpjson.url }}"><strong>{{ $ogpjson.title }}</strong></a>
                        </div>
                        <div class="rcm12" style="font-size: .8rem;">
                            {{ $ogpjson.description | truncate 50 }}</small>
                        </div>
                    </div>
                </div>
            </div>
            <div class="rcx3 rcm3 rtxc rpxs rpls">
                <a href="{{ $ogpjson.url }}">
                    <div>
                        <amp-img class="" src="{{ $ogpjson.image }}"
                            alt="{{ $ogpjson.title }}" width="75" height="75" layout="fixed"></amp-img>
                    </div>
                </a>
            </div>
        </div>
    </div>
</div>

APIサーバ (app.ts)

上記記事を参考にさせていただきました。/sizeはサイズ取得が必要となったとき用にAPIを生やしました。

Hugoでブログカードに対応する | Hugo 入門 / 解説 | nasust dev blog

import * as express from 'express';
import * as client from 'cheerio-httpcli';
import * as requestImageSize from 'request-image-size';

const app = express();

app.get("/size", (expressRequest, expressResponse, expressNext) => {
    const url = expressRequest.query.url;
    client.fetch(url, (err, $, res, body) => {
        if (err) {
            expressNext(err)
            return;
        }

        requestImageSize(url)
            .then(size => {
                const result = {
                    width: size.width,
                    height: size.height,
                };
                expressResponse.json(result);
            })
            .catch(err => console.error(err));
    });
})

app.get("/getogp", (expressRequest, expressResponse, expressNext) => {
    const url = expressRequest.query.url;
    client.fetch(url, (err, $, res, body) => {
        if (err) {
            expressNext(err)
            return;
        }

        const result = {
            exists: false,
            title: "",
            description: "",
            url: "",
            image: "",
            site_name: "",
            type: "",
        }

        const ogTitleQuery = $("meta[property='og:title']");

        if (ogTitleQuery.length > 0) {
            result.exists = true;
            result.title = $("meta[property='og:title']").attr("content");
            result.description = $("meta[property='og:description']").attr("content");
            result.url = $("meta[property='og:url']").attr("content");
            result.image = $("meta[property='og:image']").attr("content");
            result.site_name = $("meta[property='og:site_name']").attr("content");
            result.type = $("meta[property='og:type']").attr("content");
        } else {
            result.title = $("head title").text()
            result.description = $("meta[name='description']").attr("content");
        }

        expressResponse.json(result);
    });

})

app.listen(6060, () => console.log('Listening on port 6060'));

CI(Azure Pipelines)での設定

CI上でも実行できるように事前にJSON API Serverを実行するようにしました。

- script: |
        npx ts-node src/app.ts &
  displayName: 'Run json api server'

- script: |
        hugo
  displayName: 'Build content by hugo'

まとめ

今まで、HugoのgetJSONを使うときには、外部にAPIサーバを立てないといけないという固定観念がありました。 しかし、よく考えればローカルAPIサーバでも問題なく動作するので、使い方に幅が出そうです。

ただし、「動的にコンテンツを生成するのはHugoの哲学に沿っているのか」という点は少し検討すべきです。 ビルドが超高速なため、編集・確認までをシームレスに行えるのがHugoの魅力です。 getJSONを利用することでビルド速度が落ちてしまったら、魅力が半減してしまうことを懸念しています。 (getJSONはキャッシュを行うので一度情報取得してしまえば問題ない可能性もあります)

しばらくは、試しながら様子を見てみます。

B! Pocket

Related contents

TECH

2020.11.01

HugoでTailwindCSSを利用しAMP Validなページを生成する

TECH

2022.08.29

Hugoでブログカードを作成する(resources.GetRemote利用)

TECH

2022.06.29

AMP Service WorkerでPrefetch Linksを実現する

TECH

2019.10.11

AMPページの最適化〜ぼくのAMPサイトがこんなに遅い訳がない〜

TECH

2019.10.06

AMP向けのミニマルCSSフレームワーク「1BX」をHugoに導入した

TECH

2020.12.26

Hugoでブログ記事一覧ページ(ブログアーカイブページ)を作成する

TECH

2020.09.27

AMP OptimizerによるAMPのさらなる最適化

TECH

2020.08.15

Progressive Web Appを有効にする