この記事は、Progate Path コミュニティ Advent Calendar 2024 の 21 日目の記事です。
後編では、 Eleventy
を使って CSS と JavaScript をビルドする環境を用意します。途中で出てくるコマンドなどは、 Mac を前提としているため、 Windows を使っている場合は適宜読み替えてください。
Eleventy を使って CSS と JavaScript をビルドする
前編で HTML をビルドする環境の用意ができたので、次は CSS と JavaScript をビルドできるように拡張していきましょう。以下の流れで進めていきます。
- prettier を導入して、出力後の HTML を整形する
- PostCSS を導入して、 CSS の出力を最適化する
- esbuild を導入して、 JavaScript をバンドルする
もしすぐに環境を利用したい場合は、 こちら にリポジトリを用意したので利用してください。
1. prettier を導入して、出力後の HTML を整形する
前編で出力した HTML ファイルを開くと、インデントが綺麗に揃っていません。細かいことですが、これを npm run build
実行時に自動で整形できるようにしましょう。以下の流れで対応を進めます。
- prettier をインストールする
- eleventy.config.js に HTML 出力後の処理を追記する
1-1. prettier をインストールする
ターミナルで以下のコマンドを実行してください。
$ npm install --save-dev prettier
package.json
の devDependencies
に、 prettier
が追加されていれば OK です。
1-2. eleventy.config.js に HTML 出力後の処理を追記する
ファイルに以下の変更を加えます。
- ファイルの冒頭で
prettier
を呼び出す - 末尾にある出力用の
return
文の上に、 HTML 整形用の処理を追記する
// ファイル冒頭で prettier を呼び出す
const prettier = require("prettier");
// HTMLの変換後に実行される処理を追記する
module.exports = async (eleventyConfig) => {
eleventyConfig.addTransform("prettier", async (content, outputPath) => {
if (outputPath?.endsWith(".html")) {
return prettier.format(content, {
parser: "html",
printWidth: 120,
tabWidth: 2,
useTabs: false,
});
}
return content;
});
// (中略)
};
出力ファイルに変更を加えるために、 addTransform メソッドを使用します。
addTransform
は、第一引数に処理の名前を定義するための文字列を設定し、コールバック関数に処理を記述します。 prettier
処理では、拡張子が .html
だったときに、 prettier
によるフォーマットを実行し、フォーマット後のコンテンツを返しています。
ここまで出来たら、出力後のファイルが整形されるか確認してみましょう。ターミナルで以下のコマンドを実行してください。
$ npm run build
2. PostCSS を導入して、 CSS の出力を最適化する
CSS のトランスパイラとして、今回は PostCSS を導入します。また、いくつかの機能を追加したいので、以下のパッケージを導入します。
-
autoprefixer
- ベンダープレフィックスの自動付与する
-
postcss-import
-
@import
を使ってファイルの分割管理を実現する
-
-
cssnano
- 出力後のファイルを minify 化する
以下の流れで対応を進めていきます。
- 必要なパッケージをインストールする
- eleventy.config.js に HTML 出力後の処理を追記する
2-1. 必要なパッケージをインストールする
ターミナルで以下のコマンドを実行してください。
$ npm install --save-dev postcss autoprefixer postcss-import cssnano
2-2. eleventy.config.js に HTML 出力後の処理を追記する
ファイルに以下の変更を加えます。
- ファイルの冒頭で、
node:path
/autoprefixer
/postcss-import
/cssnano
を呼び出す -
prettier
の処理よりも上に、 PostCSS の処理を追記する
// ファイルの冒頭で必要なパッケージを呼び出す
const path = require("node:path");
const postcss = require("postcss");
const autoprefixer = require("autoprefixer");
const postcssImport = require("postcss-import");
const cssnano = require("cssnano");
// CSSのトランスパイル処理を追記する
module.exports = async (eleventyConfig) => {
eleventyConfig.addTemplateFormats("css");
eleventyConfig.addExtension("css", {
outputFileExtension: "css",
compile: async (inputContent, inputPath) => {
// _から始まるファイルは無視
if (path.basename(inputPath).startsWith("_")) {
return;
}
// PostCSSの処理
const result = await postcss([
postcssImport, // @importの解決
autoprefixer, // ベンダープレフィックスの自動付与
cssnano({
// CSSの最適化(本番環境用)
preset: "default",
}),
]).process(inputContent, {
from: inputPath,
to: inputPath.replace(/^src/, "dist"),
});
return async () => {
return result.css;
};
},
});
// (中略)
};
addTemplateFormats は Eleventy
に CSS ファイルを処理対象として認識させるための設定で、 addExtension
はその拡張子に対する処理を記述するためのメソッドです。(公式ドキュメントでこのメソッドの端的な説明が見つけられず、サンプルコードと説明からこうだろう、という推測になります)
PostCSS
の処理を addExtension
の中で行い、その結果を return
することで、 @import
やベンダープレフィックスが解決された状態で、かつ minify された CSS を出力するよう指示しています。
ここまで出来たら、動作確認のために CSS ファイルを作成し、ビルドコマンドを実行してみましょう。 以下の CSS ファイルを用意してください。
header {
background: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
nav {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
}
ul {
display: flex;
gap: 2rem;
list-style: none;
margin: 0;
padding: 0;
}
a {
color: #333;
text-decoration: none;
&:hover {
color: #666;
}
}
}
/* リポジトリの main.css から一部を抜粋しています */
@import "./base/_reset.css";
@import "./components/_header.css";
:root {
/* コンテンツ幅 */
--content-width-sm: 640px;
--content-width-md: 768px;
--content-width-lg: 1024px;
--content-width-xl: 1200px;
--content-width-2xl: 1536px;
/* 背景色 */
--color-background: #ffffff;
--color-background-alt: #f3f4f6;
--color-background-dark: #e5e7eb;
/* スペーシング */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
}
/* ベース */
html {
font-family: system-ui, sans-serif;
background-color: var(--color-background);
}
body {
margin: 0;
padding: 0;
}
main {
margin: auto;
padding: var(--spacing-md);
width: 100%;
max-width: var(--content-width-xl);
}
ファイルの作成後、ターミナルで以下のコマンドを実行してください。
$ npm run build
dist/styles/main.css
が生成されていれば OK です。
3. esbuild を導入して、 JavaScript をバンドルする
JavaScript のバンドルツールとして、今回は esbuild を導入します。以下の流れで進めていきます。
- esbuild をインストールする
- eleventy.config.js に JavaScript の処理を追記する
3-1. esbuild をインストールする
ターミナルで以下のコマンドを実行してください。
$ npm install --save-dev esbuild
package.json
の devDependencies
に esbuild
が追加されていれば OK です。
3-2. eleventy.config.js に JavaScript の処理を追記する
ファイルに以下の変更を加えます。
- ファイルの冒頭で
esbuild
を呼び出す - CSS の処理の下くらいに、 JavaScript の処理を追記する
// ファイルの冒頭で esbuild を呼び出す
const esbuild = require("esbuild");
// JavaScript のバンドル処理を追記する
module.exports = async (eleventyConfig) => {
// (中略)
eleventyConfig.on("eleventy.before", async () => {
const isProduction = process.env.NODE_ENV === "production"; // 環境変数で判定
const jsFiles = [
"src/scripts/main.js", // 他のファイルをここに追加可能
];
for (const inputFile of jsFiles) {
// modules 配下のファイルはスキップ
if (inputFile.includes("/modules/")) {
continue;
}
const outputFile = inputFile.replace(/^src\/scripts\//, "dist/scripts/");
// esbuild バンドル
await esbuild.build({
entryPoints: [inputFile], // バンドル対象のファイル
outfile: outputFile, // 出力ファイル名を同一に
bundle: true, // バンドル有効
minify: isProduction, // 本番環境では圧縮
sourcemap: !isProduction, // ソースマップの出力を環境変数で制御
format: "iife", // フォーマットは即時関数
platform: "browser", // ブラウザ用設定
mainFields: ["browser", "module", "main"], // モジュール解決
});
}
});
// (中略)
};
eleventy.before イベントは、 Eleventy
によるテンプレートの処理が始まる前に、独自の処理を差し込むことができるオプションです。テンプレートの処理が始まる前に、 JavaScript のバンドル処理は完了したいので、今回はこのイベントを使用してみました。
一旦、 eleventy.config.js
の全体像をまとめてみました。もしここまでの対応で不安があれば、参考にしてみてください。
eleventy.config.js
const path = require("node:path");
const postcss = require("postcss");
const autoprefixer = require("autoprefixer");
const postcssImport = require("postcss-import");
const cssnano = require("cssnano");
const esbuild = require("esbuild");
const prettier = require("prettier");
module.exports = async (eleventyConfig) => {
// CSSのビルド
eleventyConfig.addTemplateFormats("css");
eleventyConfig.addExtension("css", {
outputFileExtension: "css",
compile: async (inputContent, inputPath) => {
// _から始まるファイルは無視
if (path.basename(inputPath).startsWith("_")) {
return;
}
// PostCSSの処理
const result = await postcss([
postcssImport, // @importの解決
autoprefixer, // ベンダープレフィックスの自動付与
cssnano({
// CSSの最適化(本番環境用)
preset: "default",
}),
]).process(inputContent, {
from: inputPath,
to: inputPath.replace(/^src/, "dist"),
});
return async () => {
return result.css;
};
},
});
// JSファイルのバンドル
eleventyConfig.on("eleventy.before", async () => {
const isProduction = process.env.NODE_ENV === "production"; // 環境変数で判定
const jsFiles = [
"src/scripts/main.js", // 他のファイルをここに追加可能
];
for (const inputFile of jsFiles) {
// modules 配下のファイルはスキップ
if (inputFile.includes("/modules/")) {
continue;
}
const outputFile = inputFile.replace(/^src\/scripts\//, "dist/scripts/");
// esbuild バンドル
await esbuild.build({
entryPoints: [inputFile], // バンドル対象のファイル
outfile: outputFile, // 出力ファイル名を同一に
bundle: true, // バンドル有効
minify: isProduction, // 本番環境では圧縮
sourcemap: !isProduction, // ソースマップの出力を環境変数で制御
format: "iife", // フォーマットは即時関数
platform: "browser", // ブラウザ用設定
mainFields: ["browser", "module", "main"], // モジュール解決
});
}
});
// HTMLの変換後に実行される処理
eleventyConfig.addTransform("prettier", async (content, outputPath) => {
if (outputPath?.endsWith(".html")) {
return prettier.format(content, {
parser: "html",
printWidth: 120,
tabWidth: 2,
useTabs: false,
});
}
return content;
});
// 出力設定
return {
dir: {
input: "src",
output: "dist",
includes: "_includes",
layouts: "_layouts",
data: "_data",
},
};
};
ここまで出来たら、動作確認のために JS ファイルを作成し、ビルドコマンドを実行してみましょう。以下の JS ファイルを用意してください。
export function greet(name) {
console.log(`Hello, ${name}!`);
}
import { greet } from "./modules/greet.js";
greet("Eleventy + esbuild");
ファイルの作成後、ターミナルで以下のコマンドを実行してください。
$ npm run build
dist/scripts/main.js
が生成されていれば OK です。
まとめ
前編、後編までの対応で、 Eleventy
を使って HTML / CSS / JS ファイルをビルドする環境を作成できました。地味ですが、複雑な機能を持たないような LP の制作だったり、バックエンドの開発は別会社で…というようなケースでは、そこそこ利用できるのかなと思っています。
余力があったら、 Github Actions を使って Xserver Static にアップロードし、認証をかけて一時的なチェック用の環境を作成する手順も書いてみたいと思います。