💡

ビルド後に組み込み作業アリ用のAstro開発環境

2025/02/26に公開

はじめに

案件を受託するという仕事の性質上、書き出されるデータに制約が存在します。
CMS やフォームが含まれたWebサイトをまずは静的に組み、フロントエンドがビルドしたデータをバックエンドが編集する(組み込む)場合や、クライアントのガイドラインで assets ファイルの名前や構成が制限される場合などです。要件に合わせ、柔軟な対応が求められます。

開発環境の分類

自分の使用する開発環境を分類しました。Web アプリではなく、コーポレートサイトや LP のような Webサイトの制作を想定したものです。(個人的に好きなので Svelte を使用していますが、React や Vue を使う人は置き換えて下さい)。

この記事でターゲットにしてるのは「Astro 限定活用」の開発環境です。

GitHub

制作した開発環境のデータは以下のリポジトリにあります。
https://github.com/ymnkx/astro-with-constraints

方向性の整理

  1. バックエンドの組み込みやすさを考慮する
    • html の可読性を高くする
    • アセットファイル名にhashをつけない
    • Astro コンポーネントのスタイルのスコープは使わない
  2. できるだけ効率的に開発できるようにする
    • Astro を使用してコンポーネントで設計&開発
  3. 制約が強い場合を考慮する
    • バックアッププランを用意

assets ファイルの Build 例

最終的に、Build した assets ファイルは以下のような内容になります。

htdocs
└── assets
    ├── css
    │   ├── common.css
    │   ├── components
    │   │   ├── accordion.css
    │   │   └── section-block.css
    │   ├── second.css
    │   └── top.css
    ├── image
    │   └── sample.png
    ├── js
    │   ├── chunk
    │   │   ├── Accordion.astro.GR6HRlk6.js
    │   │   └── matchMediaController.CCgelFRS.js
    │   ├── common.js
    │   └── top.js
    └── svg
        └── icons.svg

1. CSS

記述場所

コンポーネントとして管理しやすくするために、CSS は astro ファイル内の style タグに is:global 属性を付与して書きます。

<style lang="scss" is:global>...</style>

global なので、astro ファイルごとのスコープは効きません。ユニークになる命名規則を採用する必要があります。front-matter 内で scss ファイルを import しても効果は同じなのですが、管理しやすいように global style を採用しました。

ファイル名の設定

CSS のファイル名は astro.config.mjs の assetFileNames で設定します。

astro.config.mjs
export default defineConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          assetFileNames: (info) => {
            const exNameMatchImportant = String(info.source).match(/--output-file-name-important:\s*([^}]+)/);
            const exNameMatches = [...String(info.source).matchAll(/--output-file-name:\s*([^}]+)/g)];
            const exNameMatch =
              exNameMatches.length > 0
                ? exNameMatches.map((item) => item[1].replace(/\.css$/, '')).join('-') + '.css'
                : null;
            const fileName = exNameMatchImportant
              ? exNameMatchImportant[1]
              : exNameMatch
                ? `components/${exNameMatch}`
                : info.names[0];
            return `${assetsDir}/[ext]/${fileName}`;
          },
        },
    },
});

デフォルトのままだと呼び出したファイル名に影響され、index-1.css や index-2.css といった名称になります。ファイル名をコントロールするために、CSS のカスタムプロパティ(CSS変数)を使用することにしました。source から抽出しています。

ファイル名指定用カスタムプロパティ

「ファイルの種類」によって、2種類の指定方法があります。

カスタムプロパティ名 使用CSS 説明
--output-file-name 複数ページ用CSS
--output-file-name-important 全ページ共通CSS、ページ固有CSS 優先される
:host {
  --output-file-name: sample.css;
}

CSSファイルの種類

CSS は3種類に分類できます。CSS を使用する範囲に合わせ、Astro が自動で振り分けます。

  1. 全ページ共通 CSS
  2. ページ固有 CSS
  3. 複数ページ用 CSS

あるコンポーネントが複数ページで使用され、且つ全ページにはない場合、そのコンポーネント用の CSS ファイルは個別に書き出され、該当するページで読み込まれます。これが「複数ページ用 CSS」です。

どのコンポーネントがどのページで使用されるかということを、あらかじめ全て決めるのは難しいと思います。ファイル名の指定が複数混ざった時のために、優先度の高くファイル名を設定できる --output-file-name-important を用意しました。「全ページ共通CSS」と「ページ固有CSS」で使用しています。

:host {
  --output-file-name-important: common.css;
}

注意事項

  • ビルドしたhtml内で、CSSファイルの読み込まれる順番が制御できません。
    • @layer を使って、優先度を設計する必要があります(例:reset、base、それ以外)

バックアッププラン

もし、複数ページ用 CSS をつくらず、CSS ファイルの数を制御したい場合、以下の対応が必要です。

  • CSS の記述場所を astro ファイル内の style ではなく、外部の scss ファイルに変更する。
  • 全ページ共通 CSS、ページ固有 CSS から @forward で読み込む(手動管理)。
  • 複数ページ用 CSSは、全て全ページ共通 CSS にまとめる。
    • あるいは、すべて全ページ共通 CSS として1ファイルにまとめる。

一気に管理が面倒になるので、可能な限り避けたいところです。

2. JavaScript

記述場所

ts ファイルに書きます。astro ファイル内に記述すると、コンポーネントの位置に script タグが生成されるので使用しません。
ファイルはコンポーネントと同じフォルダに置き、名前も合わせます。

└ sample/
   ├── Modal.astro
   └── Modal.astro.ts

配置方法

script タグの配置場所を指定するため、script タグは head タグ内に書きます。書き出したい js の種類に合わせて使用する Layout を変更します。たとえば、common.js と top.js を使用する場合は、以下のように設定します。

Head.astro
---
import Script_Common from '@/components/Script/Common.astro';
import Script_Top from '@/components/Script/Top.astro';
---

<head>
  ...
  <Script_Common />
  <Script_Top />
  ...
</head>

ビルドしたjsファイルの名前は、読み込んでいる Astro ファイル名に依存します。
上の Head.astro で import している「Common.astro」は命名のための Astro ファイルです。中身には scrpit タグを1つ書き、ts ファイルを読み込みます。

components/Script/Common.astro
<script src="@/scripts/common.ts"></script>

それぞれの ts ファイルで、必要なスクリプトを import します。

common.ts
import { Modal } from '@/components/Sample/Modal.astro.ts';

ファイル名の設定

js のファイル名は astro.config.mjs で設定します。

astro.config.mjs
export default defineConfig({
  build: {
    assets: `assets/chunk`,
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          entryFileNames: (info) => {
            let fileName = 'violation';
            if (info.facadeModuleId) {
              const match = info.facadeModuleId?.toLowerCase().match(/\/([^\/]+)\.astro/);
              if (match && match[1]) fileName = `${match[1]}`;
            }
            return `assets/js/${fileName}.js`;
          },
    },
});

1つ厄介な問題があります。chunk された js ファイルです。

複数箇所で使用している共通のスクリプトは、chunk として外部ファイル化されます。問題は chunk ファイルの名前を設定する chunkFileNames です。Astro でこちらを使用すると、不要なファイルがたくさん生成されます(Astro はサーバーサイドとクライアントサイドで2回の build がはしり、サーバーサイドで不要なファイルが生成されます。根本的な原因は不明)

chunk はそのまま使用することとします。ファイル名に hash がつくのですが、参照元がjsファイル内なので html に差分は生まれず、サーバーサイドの組み込み作業に影響はないからです。

バックアッププラン

もし、chunk ファイルをつくらず、jsファイルを制御したい場合、以下の対応が必要です。

  • 書き出される js ファイルは1つに制限し、すべてそこに import する。
  • (もしくは、「同じ内容のコードをコピーして、chnuk されないようにする」という方法もあるが、開発効率が悪くなるので却下とします)

3. 画像

方針

Astro の画像の扱い方は、public 画像とsrc 画像があるのですが、public 画像のみ使用します。src 画像(Astro 画像コンポーネント)は自動でサイズ指定、webp 化などができるのですが、ファイル名にハッシュがついてしまうためです。

webp

jpg や png を webp に変換する script を用意しました。ターミナルから実行します。

npm run convert-webp

デフォルトではプロジェクトルールにある convert フォルダ内で変換する設定です。プロジェクトの要件に合わせて変更可能です。

svg

たくさんのアイコン画像を扱う時に便利な svg スプライトを生成する script を用意しました。個別のsvg ファイルを1つにまとめ、symbol 化します。script は、ターミナルから実行できます。

npm run make-svg-sprite

デフォルトでは以下の設定になっています。

ファイル 場所
元svgファイル /src/svg/
svgスプライト /public/assets/svg/icons.svg

元 svg ファイルの名前がそのまま id として設定されます。汎用的な Icon コンポーネントを用意してます。use タグで配置するので、css で color の変更が可能です。

Icon.astro
---
const { name } = Astro.props;
const svgPath = `/assets/svg/icons.svg#${name}`;
---

<span class="sample-icon">
  <svg viewBox="0 0 24 24"><use href={svgPath}></use></svg>
</span>

4. Build

Astro の標準の build と別に納品データ作成用の build(delivery)を用意しました。
以下のことができます。

  • build フォルダにデータを作成(バージョン管理の対象)
  • Prettier で整形した後、空白行を削除。
  • ファイルリストを生成

delivery コマンドで実行します。

npm run delivery

NGワードチェック

Build コマンドに、NG ワードチェック機能を追加してます。
NG ワードを登録しておき、書き出されたファイル内に存在した場合、メッセージ付きのエラーを表示します。プロジェクト特有のNGワードがある場合に使用できます。

5. その他

  • npm install 時にプロジェクト名を登録できます。
    • package.json の name を上書きします。
  • 一般的なコンポーネント(Modal、Accordion、Carousel、Navigation)を追加してます。
  • GitHub Copilot 用のカスタムインタラクションを設定してます。

まとめ

Astro を使用し、「制約」と「開発しやすさ」のバランスをとった開発環境をつくりました。
CSS ファイル名用のカスタムプロパティを設定する必要があったり、JavaScript 埋め込み用の Astro コンポーネントを用意する必要があるのは面倒なのですが、ある程度の開発のしやすさは担保できたのではないかと思います。

Discussion