React Static でサンプルページをビルドした際に、 sitemap.xml
に記述されている loc
の値がルートディレクトリからの絶対パスで記述されていたのに気付いたので、対処療法的に対処しました。
現象確認
現象を確認するために React Static をセットアップします。
> npm init -y
> yarn add react-static
ローカルインストール。
> react-static create
? What should we name this project? rsps_l
? Select a template below... basic
Creating new react-static project...
## 略
[✓] Project "rsps_l" created (192s)
To get started:
cd "rsps_l"
yarn start - Start the development server
yarn build - Build for production
yarn serve - Test a production build locally
react-static create
でサイトを生成します。
import path from 'path'
import axios from 'axios'
export default {
getRoutes: async () => {
const { data: posts } = await axios.get(
'https://jsonplaceholder.typicode.com/posts'
)
return [
{
path: '/blog',
getData: () => ({
posts,
}),
children: posts.map(post => ({
path: `/post/${post.id}`,
template: 'src/containers/Post',
getData: () => ({
post,
}),
})),
},
]
},
plugins: [
[
require.resolve('react-static-plugin-source-filesystem'),
{
location: path.resolve('./src/pages'),
},
],
require.resolve('react-static-plugin-reach-router'),
require.resolve('react-static-plugin-sitemap'),
],
}
ちなみに、デフォルトの状態で static.config.js
は以上のような状態です。
> yarn build
まずはデフォルトの状態でビルドします。
図のように、 loc
タグ の値がルートディレクトリからの絶対パスになっています。
サイトマップの構造について
ここでサイトマップの構造について確認します。
<loc>
required URL of the page. This URL must begin with the protocol (such as http) and end with a trailing slash, if your web server requires it. This value must be less than 2,048 characters.
「ページのURL。このURLはプロトコル( http
のような)から始まるURLでなければならない、また、Webサーバが必要とするならばスラッシュ /
終わりにしなければならない。この値は2,048文字未満にすること。」というところでしょうか。
……ということは、デフォルトの状態ではよろしくなさそうですね。
余談
ちなみに sitemap.org の日本語訳ページでは
<loc>
必須 ページの URL です。 ウェブ サーバーによっては、http などのプロトコルから始め、末尾にスラッシュを含める必要があります。 この値は 2,048 文字以下で指定する必要があります。
となっていて、 if your web server requires it
の部分が先頭に来てしまっているため、個人的には分かりづらいと感じました……。
対処
まずはどうやってカスタマイズできるか確認。
import path from 'path'
import axios from 'axios'
export default {
getRoutes: async () => {
const { data: posts } = await axios.get(
'https://jsonplaceholder.typicode.com/posts'
)
return [
{
path: '/blog',
getData: () => ({
posts,
}),
children: posts.map(post => ({
path: `/post/${post.id}`,
template: 'src/containers/Post',
getData: () => ({
post,
}),
})),
},
]
},
plugins: [
[
require.resolve('react-static-plugin-source-filesystem'),
{
location: path.resolve('./src/pages'),
},
],
require.resolve('react-static-plugin-reach-router'),
[
// react-static-plugin-sitemap にオプションを追加
require.resolve('react-static-plugin-sitemap'),
{
getAttributes: route => {
// とりあえず route に何が渡ってきているか確認
console.log(route)
}
}
]
],
}
react-static-plugin-sitemap
の部分にコールバックで console.log
を仕込んで、 route
の中身を確認します。
> yarn build
## 略
{
path: 'blog/post/98',
template: '__react_static_root__/src/containers/Post',
getData: [Function: getData],
sitemap: { noindex: false },
data: {
post: {
userId: 10,
id: 98,
title: 'laboriosam dolor voluptates',
body: 'doloremque ex facilis sit sint culpa\n' +
'soluta assumenda eligendi non ut eius\n' +
'sequi ducimus vel quasi\n' +
'veritatis est dolores'
}
},
sharedHashesByProp: {},
sharedData: {}
}
{
path: 'blog/post/99',
template: '__react_static_root__/src/containers/Post',
getData: [Function: getData],
sitemap: { noindex: false },
data: {
post: {
userId: 10,
id: 99,
title: 'temporibus sit alias delectus eligendi possimus magni',
body: 'quo deleniti praesentium dicta non quod\n' +
'aut est molestias\n' +
'molestias et officia quis nihil\n' +
'itaque dolorem quia'
}
},
sharedHashesByProp: {},
sharedData: {}
}
{
path: 'blog/post/100',
template: '__react_static_root__/src/containers/Post',
getData: [Function: getData],
sitemap: { noindex: false },
data: {
post: {
userId: 10,
id: 100,
title: 'at nam consequatur ea labore ea harum',
body: 'cupiditate quo est a modi nesciunt soluta\n' +
'ipsa voluptas error itaque dicta in\n' +
'autem qui minus magnam et distinctio eum\n' +
'accusamus ratione error aut'
}
},
sharedHashesByProp: {},
sharedData: {}
}
出力結果から、 path
が loc
に出力されていそうなことが分かりました。
今回は諸事情により時間をあまり割くことができないので、手っ取り早く定数でサイトURLを定義してそれを渡してURLを組み立てることにしました。
import path from 'path'
import axios from 'axios'
// オリジン情報を定義
const originSiteURL = 'https://example.com/'
export default {
getRoutes: async () => {
const { data: posts } = await axios.get(
'https://jsonplaceholder.typicode.com/posts'
)
return [
{
path: '/blog',
getData: () => ({
posts,
}),
children: posts.map(post => ({
path: `/post/${post.id}`,
template: 'src/containers/Post',
getData: () => ({
post,
}),
})),
},
]
},
plugins: [
[
require.resolve('react-static-plugin-source-filesystem'),
{
location: path.resolve('./src/pages'),
},
],
require.resolve('react-static-plugin-reach-router'),
[
// react-static-plugin-sitemap にオプションを追加
require.resolve('react-static-plugin-sitemap'),
{
getAttributes: route => ({
loc: new URL(route.path, originSiteURL).href
})
}
]
],
}
コールバックの戻り値に loc
を指定してデフォルトを上書きします。
これで yarn build
。
結果、意図通りプロトコル始まりのサイトURLに書き換えることができました。
参考
react-static-plugin-sitemap
sitemap.xml
参考
この記事で「プロトコル始まりではない絶対パスではまずいのでは?」と気付き。
URL組み立て
ここで path
モジュールではなく ECMAScript の new URL()
を使うと良い、というレスを見て採用。