Astroで定数管理をするならvite.defineを設定する
Astroで個人ブログを開発するとき、サイトタイトル・共通のメッセージ・CSSクラス名など、繰り返し使う値が出てくる。
constants.tsを用意して定数管理をしていたが、ビルド後にファイルが残ることにいい気がしなかった。この記事では、定数用のファイルをつくらずに定数を管理する方法を紹介する。
vite.defineを使う
Astroは、開発サーバーの起動や本番用ビルドに内部的にViteを利用している。そして、Astroの設定ファイルであるastro.config.mjs
では、Viteの設定をカスタマイズできる。
Viteの設定オプションの一つにdefineがある。ビルド時にコード中の特定の識別子1を、指定した値で置き換える機能を提供するものだ。
astro.config.mjs
で定義した定数を、プロジェクト内のAstroファイルやJavaScript/TypeScriptファイルで、import
文なしに利用できる。
astro.config.mjsの設定方法
import { defineConfig } from "astro/config";
export default defineConfig({ vite: { define: { // サイトタイトルを定義 SITE_TITLE: JSON.stringify("ウェブサイトの名前"), // CSSのIDを定義 CSS_ID_CONTAINER: JSON.stringify("container"), // アイコンのサイズを定義 ICON_SIZE: 64, // 環境変数を使って開発モードかどうかを判定 IS_DEVELOPMENT: process.env.NODE_ENV === "development", }, },});
vite.defineで定数を設定するコード例
defineオブジェクトのキーが定数名になる。
文字列を定義する場合は、JSON.stringify()
を使う必要がある。boolean
やnumber
はそのまま指定できる。
TypeScriptを使用している場合、defineで定義した定数はそのままでは型エラーとなる。型チェックを通すためには、型定義ファイルに定義を追加する必要がある。
declare const SITE_TITLE: string;declare const CSS_ID_CONTAINER: string;declare const ICON_SIZE: number;declare const IS_DEVELOPMENT: boolean;
env.d.tsに定数を定義するコード例
エラーが出なくなり、import
文なしでVSCodeなどのエディタの補完機能を利用できるようになる。
以下の画像では、わかりやすさのために__CONST__
プリフィックスをつけて定数を定義した。

型定義ファイルに定数を自動で定義する
astro.config.mjs
とenv.d.ts
の2つのファイルで定数を管理するのは面倒だった。なので、astro.config.mjs
が更新されたら、env.d.ts
も更新されるようにする。
Viteの設定オプションの一つにpluginsがある。適切なプラグインをつくって登録すれば、astro.config.mjs
が読み込まれたときにenv.d.ts
ファイルを書き換えられる。言い換えれば、両ファイルを同期できる。
ただし、著者はViteのプラグインについて詳しくないので、以下のコードは参考程度にしてほしい。
import type { ViteUserConfig } from "astro";import fs from "fs";import path from "path";
export default function generateEnvTypes() { const filename = "vite-plugin-define-constants";
return { name: filename, config: (config: ViteUserConfig) => { // ファイルの読み込み const envPath = path.resolve(process.cwd(), "src/env.d.ts"); const lines = fs.readFileSync(envPath).toString("utf-8").split("\n");
// 生成された行が始まる印となる文字列 const mark = `// generated by ${filename}.ts`; let startIndex = lines.findLastIndex((line) => line === mark);
if (!config.define) { return; }
// この配列を印の行の後に追加する const newLines = [];
newLines.push( ...Object.entries(config.define) // .envとAstroフレームワークが定義したものを除外する .filter( ([key, value]) => !( key.startsWith("import.meta.env") || key.startsWith("__ASTRO_INTERNAL") ) ) .map(([key, value]) => `declare const ${key}: ${typeof value};`) );
// 印が見つからなかった場合 if (startIndex < 0) { // 印を先頭に追加する lines.push(mark, "\n", ...newLines); } else { // 印以降を置き換える lines.splice(startIndex + 1, lines.length - startIndex, ...newLines); }
fs.writeFileSync(envPath, lines.join("\n"), "utf-8");
return config; }, };}
import { defineConfig } from "astro/config";import generateEnvTypes from "./vite-plugin-define-constants";
export default defineConfig({ vite: { define: {…}, plugins: [generateEnvTypes()], },});
vite pluginを登録したコード例
プラグインを定義したファイルは./src/
直下に配置した。
npm run dev
で開発サーバーを実行したときと、astro.config.mjs
を保存したときにenv.d.ts
が更新される。
定数管理するほかの方法と注意点
グローバルな定数を管理する方法は3つある。
.env
ファイルに定義するastro.config.mjs
のvite.define
に定義するconstants.ts
に定義する
それぞれの特徴を比較したのが以下の表になる。
方法 | 特徴 | デメリット |
---|---|---|
.env | APIキーなどの機密情報を定義するのに適している | import.meta.env でアクセスするためコードが長くなるデプロイ先で再定義が必要な場合がある |
vite.define | ビルド後のファイルが減る | 型定義が必要 |
constants.ts | 型定義しなくても自然に扱える | ビルド後のファイルが増える |
まとめ
vite.define
は、特にビルド時に値が確定する定数・グローバルにアクセスしたい設定値・公開してもいい環境変数と連動させたい値を管理するのに便利だ。
astro.config.mjs
のvite.define
オプションを利用することは、定数管理の選択肢の一つだ。ビルド時に値を埋め込むので、シンプルに定数を扱える。
なお、vide.define
ではstring
・number
・boolean
だけでなく、object
も定義できるようだ。しかし、型定義ファイルを自動化したかったこともあり、面倒だったのでシンプルな形にした。
また、今回の方法を応用すれば、Lessファイル内の変数やCSSのクラス名も同期できた。
脚注
-
グローバル定数のようなもの ↩