水たまりは希望を写している

Astro で限りなく内容が似ている複数のウェブサイトを作るには

Astro の設定ファイル。

限りなく内容が似ている複数のウェブサイト」というものを生成したいときがある。例えば、以下のようなシチュエーションだ。

  • 複数の EC サイトのサーバースペースに置く静的な HTML を作りたい (けど、リンク先などが完璧に同じではない)
  • ページを、AB テストなどのために複数の場所に配置したい

静的サイトジェネレーターの側面を持つ Web フレームワークの Astro で、これが実現できるかどうか試してみよう。

環境変数を読み取って、Vite の定数をセットしてみる

Astro は Node.js 上で動いているので、process.env から環境変数を取得することができる。

// @ts-check
 import { defineConfig } from 'astro/config';
 
 const param = process.env.PARAM ?? 'default';
 const setting = {
     'foo': {
         name: 'foo foo',
         url: 'https://aioilight.space/'
     },
     'bar': {
         name: 'bar bar',
         url: 'https://wangel.aioilight.space/'
     }
 }[param] || {
     name: 'default default',
     url: 'https://example.com'
 };
 
 export default defineConfig({
     outDir: './dist/' + param,
     vite: {
         define: {
             '__SETTING__': setting
         }
     }
 });

astro.config.mjs で process.env から環境変数を取得し、環境変数で可変させたい設定のオブジェクトを生成する。今回は環境変数 PARAM があると想定する。PARAM の値は process.env.PARAM に入っている。環境変数が設定されていなかったときのために、デフォルト値 'default' を用意しておく。変数 setting に設定を意味するオブジェクトを代入する。用意されていない値が来たときは、 undefined になるので OR 演算子でデフォルトのオブジェクトを代入させる。

Astro は Vite を使っている。defineConfig() から、Vite の設定も変えることができる。Vite には define という設定があり、ここでグローバル定数を設定することができる。vite.define にそのままオブジェクトを突っ込む。

/// <reference path="../.astro/types.d.ts" />
 
 declare const __SETTING__: {
     name: string,
     url: string
 }

このままではこのグローバル定数への型情報は失われてしまっているので、Astro のファイルで型推論を効かすために、env.d.ts で型を定義しておく。

---
const { name, url } = __SETTING__;
---
<body>
	<h1>{name}</h1>
	<p>{url}</p>
</body>

Astro のファイルでグローバル定数を使うときは、こんな感じ。

astro.config.mjs のほうで outDir とか site とかも可変させれば、今回の課題はクリアできる。

すべてをビルドする npm scripts

{   
  "scripts": {
    "allbuild": "for %S in (foo bar) do @set PARAM=%S & npm run build"
  }
}

このような npm scripts を作ると全部一気にビルドできる。これは Windows での場合で、環境変数をセットする都合上シェルによって構文が異なる。Windows 11 でも PowerShell ではなく cmd が使われるので VSCode 上のターミナルとも構文が異なる可能性がある。

こんな感じ。

この記事を書いた人

AioiLight

Web とか Android とかをやってる人。アニメ・ゲームが好きなはずなのに消費しきれない毎日。

Twitter (@aioilight)