PlayLogic
記事一覧 タグ一覧
Astroで定数管理をするならvite.defineを設定するのサムネイル
もくじ

Astroで定数管理をするならvite.defineを設定する

Astroで個人ブログを開発するとき、サイトタイトル・共通のメッセージ・CSSクラス名など、繰り返し使う値が出てくる。

constants.tsを用意して定数管理をしていたが、ビルド後にファイルが残ることにいい気がしなかった。この記事では、定数用のファイルをつくらずに定数を管理する方法を紹介する。
開発者ツールのSourcesタブにconstants.tsが含まれている画像
開発者ツールのSourcesタブにconstants.tsが含まれている画像

vite.defineを使う

Astroは、開発サーバーの起動や本番用ビルドに内部的にViteを利用している。そして、Astroの設定ファイルであるastro.config.mjsでは、Viteの設定をカスタマイズできる。

Viteの設定オプションの一つにdefineがある。ビルド時にコード中の特定の識別子1を、指定した値で置き換える機能を提供するものだ。

astro.config.mjsで定義した定数を、プロジェクト内のAstroファイルやJavaScript/TypeScriptファイルで、import文なしに利用できる。

astro.config.mjsの設定方法

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()を使う必要がある。booleannumberはそのまま指定できる。

TypeScriptを使用している場合、defineで定義した定数はそのままでは型エラーとなる。型チェックを通すためには、型定義ファイルに定義を追加する必要がある。

src/env.d.ts
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__プリフィックスをつけて定数を定義した。

設定した定数のインテリセンスがVS Codeで表示された画像
設定した定数のインテリセンスがVS Codeで表示された画像

型定義ファイルに定数を自動で定義する

astro.config.mjsenv.d.tsの2つのファイルで定数を管理するのは面倒だった。なので、astro.config.mjsが更新されたら、env.d.tsも更新されるようにする。

Viteの設定オプションの一つにpluginsがある。適切なプラグインをつくって登録すれば、astro.config.mjsが読み込まれたときにenv.d.tsファイルを書き換えられる。言い換えれば、両ファイルを同期できる。

ただし、著者はViteのプラグインについて詳しくないので、以下のコードは参考程度にしてほしい。

vite-plugin-define-constants.ts
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;
},
};
}
astro.config.mjs
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つある。

  1. .envファイルに定義する
  2. astro.config.mjsvite.defineに定義する
  3. constants.tsに定義する

それぞれの特徴を比較したのが以下の表になる。

方法特徴デメリット
.envAPIキーなどの機密情報を定義するのに適しているimport.meta.envでアクセスするためコードが長くなる
デプロイ先で再定義が必要な場合がある
vite.defineビルド後のファイルが減る型定義が必要
constants.ts型定義しなくても自然に扱えるビルド後のファイルが増える

まとめ

vite.defineは、特にビルド時に値が確定する定数・グローバルにアクセスしたい設定値・公開してもいい環境変数と連動させたい値を管理するのに便利だ。

astro.config.mjsvite.defineオプションを利用することは、定数管理の選択肢の一つだ。ビルド時に値を埋め込むので、シンプルに定数を扱える。

なお、vide.defineではstringnumberbooleanだけでなく、objectも定義できるようだ。しかし、型定義ファイルを自動化したかったこともあり、面倒だったのでシンプルな形にした。

また、今回の方法を応用すれば、Lessファイル内の変数やCSSのクラス名も同期できた

脚注

  1. グローバル定数のようなもの

ブログのホームに移動する ホームのアイコン この記事の最上部までスクロールする 上方向の矢印のアイコン