これの続きです。
おさらい
詳しい議論は前回のブログを参照してもらうとして、blogdownのサイトをビルドする3つの関数の違いについて再掲しておきます。Hugoのテーマのテンプレートに埋め込まれている{{ .Site.BaseURL }}に相対URLが入るか絶対URLが入るか、Rmdをどこまでknitするか、といった点に違いがあります。
| 関数 | 目的 | {{ .Site.BaseURL }} |
Rmdをknitするか |
|---|---|---|---|
serve_site() |
手元でのプレビュー | /(つまり相対URL) |
まだknitしていないものだけknitする |
hugo_build() |
公開用 | config.tomlで設定したもの(つまり絶対URL) |
knitしない |
build_site() |
公開用 | config.tomlで設定したもの(つまり絶対URL) |
すべてのRmdをknitしなおす |
で、結論としては、
解決策3. HTMLではなくMarkdownファイルをgit管理下に入れ、HTMLへの変換はリモートのHugoにやってもらう
が一番いいでしょう。でもどうやって??という感じでしたが、やり方がなんとなくわかったので荒く説明を残しておきます。
build_site()で独自のビルドスクリプトを使う
build_site()は、R/build.Rというファイルがあるとサイト生成前にそれを実行します。また、method='custom'だと通常のサイト生成手順はスキップされ、R/build.Rだけが実行されるようになります。
For
method = 'custom',build_site()will not process any R Markdown files, nor will it call Hugo to build the site. No matter which method you choose to use,build_site()will always look for an R script/R/build.R(blogdown: Creating Websites with R Markdown)
method='custom'は引数でも指定できますが、blogdown.methodというオプションでデフォルトを設定できます。ワーキングディレクトリの.Rprofileに書いておきましょう。
if (file.exists('~/.Rprofile')) sys.source('~/.Rprofile', envir = environment()) options(blogdown.method = 'custom')
(参考: https://github.com/rbind/yihui/blob/68716cccb7c5b2bdff7a01b9e6321da43ca22807/.Rprofile#L4)
R/build.Rをつくる
これで通常のサイト生成処理は走らなくなったので、独自のビルドスクリプトをつくっていきましょう。単純化したものはこんな感じです。
# RMarkdownファイルを列挙 rmds <- list.files("content", "[\\.]Rmd$", recursive = TRUE, full.names = TRUE) for (rmd in rmds) { # .Rmdを.mdに置き換えたファイル名 wo_ext <- tools::file_path_sans_ext(rmd) md <- glue::glue("{wo_ext}.md") # mdがRmdより新しい場合はすでにknit済みなのでスキップ if (file.exists(md) && utils::file_test("-ot", rmd, md)) { message(glue::glue("skip {rmd}")) next } # RmarkdownをMarkdownに変換 knitr::knit(input = rmd, output = md, encoding = "UTF-8") } # Hugoによるサイトの生成も走らないので自分でhugo_build()を呼び出す必要がある blogdown::hugo_build()
rmarkdownではなくknitrを直接使っていることに気付いたでしょうか。rmarkdown::render()にmd_document()を指定するとかでもいいようなところですが、あとで直接チャンクオプションを指定するのでknitr::knit()にしています。
さて、これでMarkdownファイルの生成はできますが、これだとまだうまく動きません。順を追ってスクリプトを改良していきましょう。
local引数を受け取る
local引数は、サイトの生成を絶対URLか相対URLかどちらでやるかを指定するオプションです。build.Rにはこれが文字列として渡されますが、上のスクリプトだと受け取れないので、build_site()でもserve_site()でも絶対URLになってしまいます(つまり、ローカルでプレビューしようとしても本物のサイトに飛ばされてしまう)。
on.exit(run_script('R/build.R', as.character(local)), add = TRUE)
(https://github.com/rstudio/blogdown/blob/8322ade5e38c80211ae856a9189ceb267e40dfc8/R/render.R#L37)
てことで、引数に取れるようにしましょう。
local <- commandArgs(TRUE)[1] == 'TRUE' # (略) blogdown::hugo_build(local = local)
(参考: https://github.com/rbind/yihui/blob/68716cccb7c5b2bdff7a01b9e6321da43ca22807/R/build.R#L2-L3)
base.dir、base.url、fig.pathを指定する
ちょっと説明が面倒なので以下を読んでもらうとして、このままだと画像がうまく表示できません。
これは、デフォルトだと画像はfigure/ディレクトリにつくられますが、これはHugoが感知するディレクトリではないので画像がリンク切れになってしまいます。static/ディレクトリの下に入れておけばコピーされるので、Rmdのファイル名に応じてディレクトリを掘って、そこに置くようにします。
また、base.dirとbase.urlもうまい具合に調整します。base.dirをstatic/にして、base.urlを/にしておくとうまくいくみたいでした。
knitr::opts_knit$set( base.dir = normalizePath("static/", mustWork = TRUE), base.url = "/" ) for (rmd in rmds) { # (略) knitr::opts_chunk$set( fig.path = glue::glue("post/{basename(wo_ext)}_files/figure-html/") ) knitr::knit(input = rmd, output = md, encoding = "UTF-8") }
あと、static/をbase.dirにした関係上、このままだとキャッシュもコピーされてしまうので、cache.pathも別の場所を指定しておいた方が無難な気がします。
これでローカルのプレビューも動くようになったはずです。私の場合、最終的に出来上がったbuild.Rはこんな感じでした。参考までに。
public/を.gitignoreに入れる
これはやらなくてもいいんですが、public/はリモートのHugoによって生成させるので、むしろレポジトリにコミットがあると邪魔になります。git管理下から外してしまいましょう。
Netlify(とrbind?)を使う
で、「リモートのHugo」って結局なんなのよ?というところですが、それはNetlifyしかない気がします。このへんも以前書いたので参考にしてください。