ViteとDockerにてシンプルなHTML(EJS)/CSS(Sass)/JSコーディング環境を構築する方法

# 1.はじめに - 概要と作成した背景
Webpackのビルドの遅さが気になっていたが、そのうちTurbopackやRspackなどの開発が進めばそのあたりは解決すると思っていて放置していたが、時間ができたので試しに[Vite](https://ja.vitejs.dev/)でやったらすんなりできたので記事にまとめてみました。

:::note info

開発サーバーでEJSのComponentパーツを更新してもLiveReloadされない対処の説明もれがありましたので追記しました。
:::

:::note warn
2023.4.21
globモジュールが9.x.xから10.x.xにアップデートしたようで、その関係で若干の読み込み方の変更があります。
参考URL: [nodejs | glob-npm](https://www.npmjs.com/package/glob)

:::

## 普通のWebサイト向けの開発環境の要件定義（自分専用）

1. HTMLをEJSなどのテンプレートエンジンにて作成できる
1. 複数のHTMLを作成することができ、ディレクトリ構成もそのまま反映する
1. HTMLから各ファイル(CSS、JS、画像)へのパスは相対パスにする
1. Sass(SCSS）をコンパイルができる
    1. 任意の複数のCSSに書き出しすることができる
    1. autoprefixerにてベンダープレフィックスの自動追加
    1. 散らばったメディアクエリをまとめる（postcss-sort-media-queries）
1. JavaScriptのモジュールバンドラー機能
1. JavaScriptのminify化と共通使用するモジュールを分離して独立ファイルにする
1. 作業環境をDocker内に閉じ込めて、どの環境でも同じように構築できるようにする

という形の開発環境を作成します。
最終的なディレクトリ構成は以下のようになります。

```bash
・~root #任意の場所の作業ディレクトリのルート
┗　docker #Dockerfile用ディレクトリ
┃ ┣ node
┃ ┗　　Dockerfile
┗　　www
  ┗　　html #デプロイ先のディレクトリ
    ┗　　assets
      ┣ css
      ┣ images
      ┗　　js #書き出されたJSファイルが格納される
        ┗　　modules  #書き出されたJSのmoduleファイルが格納される
      ┗　　css
        ┗　　style.css
  ┗　　src #作業用ディレクトリ
  　　　┗　　index.html
  　　　┗　　aboutus
    　　　┗　　index.html
  　　　　┗　　js
  　  　　┣ main.js #Viteが作成したファイル
  　  　　┗　　[使用するJavaScriptの各ファイル名など].js
  　　　　┗　　lib
    　　　┗　　components #EJSで使用するHTMLコンポーネントファイルを格納
    　　　　　　　┗　　metadata.ejs
     　　┗　　pagedata #EJSで使用するデータ(JSON）ファイルを格納
    　　　　　　　　┗　　pagedata.json
  　　　　　┗　　public #public ディレクトリ
  　  　　　　┗　　assets
    　  　　　　┗　　images #使用する画像を入れておく
  　　　　　　┗　　scss #構造は任意で
    　　　　　┗　　css
      　　　　　┗　　style.scss
    　└・components
      　└・_[各コンポーネントファイル名].scss
  └・.gitignore
┣ docker-compose.yml 
└・.env #Dockerのためのファイル
```

# 2.Docker環境の構築
 **※Dockerを使わないという方はここは飛ばしてください。**

DockerにてNode用作業のコンテナとサーバーコンテナを作成します。
まずは以下のようなディレクトリ構成を作成します。
```bash
・~root #任意の場所の作業ディレクトリのルート
└・docker #Dockerfile用ディレクトリ
 └・node
   └・Dockerfile
└・www
  └・html
    └・index.php #Docker起動チェックのため
└・docker-compose.yml
└・.env
```

wwwディレクトリ内にあるindex.phpはdockerコンテナがきちんと起動できたかチェックするためのファイルとなります。

```php:index.php
<?php
// 例) 以下のようにするとサーバーの情報が表示されます。
    phpinfo();
?>
```

## Node用コンテナのイメージの作成するための設定
Node用のコンテナはDocker公式のイメージを使用しても良いですが、Node管理用のツールを自由に設定できるようにDockerfileからイメージを構築するようにしています。
今回はnodebrewを使って管理するように設定しています。

```Dockerfile
FROM debian
RUN set -ex; \
    \ 
    apt-get update; \
    apt-get install -y wget ;\
    apt-get install -y vim;

# nodebrew + nodejs
ENV NODE_VERSION v18.15.0
RUN wget git.io/nodebrew
RUN perl nodebrew setup
ENV PATH $HOME/.nodebrew/current/bin:$PATH
RUN echo 'export PATH="$HOME/.nodebrew/current/bin:$PATH"' >> $HOME/.bashrc
RUN . $HOME/.bashrc && nodebrew install-binary $NODE_VERSION
RUN . $HOME/.bashrc && nodebrew use $NODE_VERSION
```
使用するNodeのバージョンは```ENV NODE_VERSION v18.15.0```の箇所で指定していますので、使いたいバージョンを指定してください。

## docker-compose.ymlの作成
ざっくりとっと以下のような設定をします。

```yml:docker-compose.yml
version: '2'
services:
  httpd:
    container_name: httpd-${CONTAINER_NAME}
    hostname: httpd
    image: php:8.1-apache
    ports:
      - "80:80"
      - "443:443"
    privileged: true
    volumes:
       - "./www/html:/var/www/html"
  node-js:
    container_name: node-js-${CONTAINER_NAME}
    build: ./docker/node
    image: node/nodejs-${CONTAINER_NAME}
    links:
      - httpd
    volumes:
      - "./www:/var/www"
    ports:
      - "3000:3000"
      - "3001:3001"
    tty: true
```
```env:.env
# ここを開発コンテナ独自の文字列にする
CONTAINER_NAME=[nodeコンテナ用の任意の文字列に（例:vite_devなど）]
```

.envファイルにて独自のコンテナ名を一括で設定できるようにしています。

## Node用Docker imageのbuildとコンテナの作成
ターミナルで以下のコマンドを実行します。
```bash
$ docker compose build --no-cache
```

buildが成功したら次にコンテナを作成します。
```bash:terminal
$ docker compose up -d
```

エラーがなければ、```http://[マシンのローカルIPアドレス]/```とサーバーの設定内容が表示されます。

# 3.Vite設定
Viteの設定は、**vite.config.js**で行います。また、PostCSSも使うので**post.config.cjs**の設定もしていきます。

:::note warn
post.config.js内の設定でプラグインの読み込みなどはでrequire()を使うとエラーになります。
理由としては、Viteは基本的にNative ES Moduelsによって動作しますので、require()は使えません。
ファイルによってはrequire()を使えるようにするには、そのファイルをCommonJSとして認識させる必要があるため、拡張子を.cjsとします。
:::

### 最終的なVite設定ファイルの内容
最初に書いてしまいますが、最終的に各ファイルの設定は以下のようになります。

:::note warn
2023.4.21
globモジュールが9.x.xから10.x.xにアップデートしたようで、その関係で若干の読み込み方の変更があります。
参考URL: [nodejs | glob-npm](https://www.npmjs.com/package/glob)
:::

```diff_javascript:vite.config.js
import { defineConfig } from 'vite';
- import glob from 'glob';//ワイルドカードを使って各ファイルの名前を取得し一括で登録するため
+ import {globSync} from 'glob';//ワイルドカードを使って各ファイルの名前を取得し一括で登録するため
import path from 'node:path';//上記の実行次にnpmのpathを利用
import { fileURLToPath } from 'node:url';//上記の実行時にURLをpathに変更させるため
import liveReload from 'vite-plugin-live-reload';//Dev時のファイルリロード監視に任意のファイルを追加できるようにするため
import { ViteEjsPlugin } from "vite-plugin-ejs";//EJS用プラグイン
import json from "./src/pagedata/data.json";//EJSでのデータ（json）を読み込む

//** ↓JS、SCSSなどの各ファイルの名称、path情報を配列に格納する設定 */
- const inputJsArray = glob.sync('src/**/*.js', { ignore: 
+ const inputJsArray = globSync('src/**/*.js', { ignore: 
['node_modules/**','**/modules/**','**/html/**'] }).map(file => {
  return [
    path.relative(
      'src/js',
      file.slice(0, file.length - path.extname(file).length)
    ),
    fileURLToPath(new URL(file, import.meta.url))
  ]
});
- const inputHtmlArray = glob.sync(['src/**/*.html'], { ignore: ['node_modules/**'] 
+ const inputHtmlArray = globSync(['src/**/*.html'], { ignore: ['node_modules/**']  
}).map(file => {
  return [
    path.relative(
      'src',
      file.slice(0, file.length - path.extname(file).length)
    ),
    fileURLToPath(new URL(file, import.meta.url))    
  ]
});
- const inputScssArray = glob.sync('src/scss/**/*.scss', { ignore: ['src/scss/**/_*.scss']
+ const inputScssArray = globSync('src/scss/**/*.scss', { ignore: ['src/scss/**/_*.scss'] }).map(file => {
  return [
    path.relative(
      'src',
      file.slice(0, file.length - path.extname(file).length)
    ),
    fileURLToPath(new URL(file, import.meta.url))    
  ]
});

/**
*　各ファイル情報の配列をまとめて、Objectにする設定
*/
const inputObj = Object.fromEntries(inputJsArray.concat(inputHtmlArray, inputScssArray));
console.log(inputObj);

/**
* Viteの設定
*/
export default defineConfig({
  root: './src', //開発ディレクトリ設定
  base: './',//各ファイルのPathを絶対パスから相対パスにするようにするため
  build: {
    outDir: '../html', //出力場所の指定
    emptyOutDir: true,//build時に出力先ディレクトリを空にする
    rollupOptions: {//rollupOptionsにて出力ファイル名を元ファイルを元にする設定をする
      input: inputObj,
      output: {
        entryFileNames: `assets/js/entry-[name].js`,//JSファイルの出力設定
        chunkFileNames: `assets/js/modules/[name].js`,//chunkファイルをmoduleディレクトリに入れる
        assetFileNames: (assetInfo) => {
          if (/\.( gif|jpeg|jpg|png|svg|webp| )$/.test(assetInfo.name)) {
            return 'assets/images/[name].[ext]';//画像アセットの出力設定
          }
          if (/\.css$/.test(assetInfo.name)) {
            return 'assets/css/[name].[ext]';
          }
          return 'assets/[name].[ext]';
        },
      }
    }
  },
  server: {
    port: 3000,//開発サーバーは
    host: true,//Dockerなどの仮想から外に出すためには　host:trueとする
    strictPort: true,//
    watch: {
      usePolling: true //Dockerなどの仮想の場合この設定をしておくと吉
    }
  },
  plugins: [
    liveReload(['components/**/*.ejs']),//開発サーバーのライブリロードに任意のファイルを追加する設定
    ViteEjsPlugin(json),//ViteのEJSプラグインの設定
  ]
});
```
```javascript:postcss.config.cjs
module.exports = () => ({
    plugins: 
        [
            require('autoprefixer')({}),
            require('postcss-sort-media-queries')({
                    sort: 'mobile-first'
                }
            ),
            require('postcss-url')({
                url: 'inline',
                maxSize: 200,
            }),
            
        ]
})

```

## Viteのインストール

1. Dockerコンテナへのアプリのインストールはdocker composeを使うとコンテナ外からもできますが、今回はコンテナ内に入って作業します。

    ```bash:Terminal
    # Dockerコンテナに入ります。コンテナ名がわからない場合は docker compose psで確認
    $ docker exec -it [Nodejsコンテナの名前] /bin/bash
    
    #dockerコンテナ内に入ったら作業ディレクトリに移動
    /# cd /var/www/
    
    #viteの初期化
    /var/www# npm init vite@latest
    #初期化を実行するとプロンプトで質問がくるので設定をする
    
    #まずはプロジェクト名を聞いてくるのでsrcとする
    ? Project name: › src
    
    #vanilaを選択する
    ? Select a framework: › - Use arrow-keys. Return to submit.
    ❯   vanilla
        vue
        react
        preact
        lit
        svelte
    
    #JavaScriptかTypeScriptoか聞いてくるのでJavaScriptを選択しておく
    ? Select a variant: › - Use arrow-keys. Return to submit.
    ❯   JavaScript
        TypeScript
    ```

1. 必要な各ファイルがsrcディレクトリに作成されます。
    ```bash
    //...
    └・www
      └・src
        └・.gitignore
        └・counter.js
        └・index.html
        └・javascript.svg
        └・main.js
        └・package.json
        └・public
        └・style.css
    
    ```
    ```package.json```と```.gitignore```を上のディレクトリに移動させて、そこでインストールを行います。
    
    ```bash:Terminal
    # 必要なファイル移動
    /var/www# mv ./src/package.json ./ [enter]
    /var/www# mv ./src/.gitignore ./ [enter]
    #インストール
    /var/www# npm install
    ```
   
1. まずは```vite.config.js```にベーシックな設定をします。

    ```javascript:ベースとなるvite.config.js
    import { defineConfig } from 'vite';

    export default defineConfig({
      root: './src', //開発ディレクトリ設定
      base: './',//相対パスにするための./とする
      build: {
        outDir: '../html', //出力場所の指定
      },
      server: { //Docker環境（仮想環境）なので以下の設定が必要
        port: 3000,//Dockerの設定でPortは3000にしています
        host: true,//Dockerなどの仮想から外に出すためには　host:trueとする
        strictPort: true,//ポートがすでに使用されている場合に、次に使用可能なポートを自動的に試さない設定にしておきます
        watch: {
          usePolling: true//Dockerなどの仮想の場合この設定をしておくと吉
        }
      }
    });
    
    ```

1. ```npm run dev```や```npm run build```テストをしてみます。
    ```bash:Terminal
     # 開発サーバーの起動チェックする場合
    /var/www# npm run dev
    ```
    Terminalでエラーが表示されなく、且つブラウザで```http://[マシンのIPアドレス]:3000```で表示されれば成功です。
    ```bash:Terminal
    # 書き出しチェック
    /var/www# npm run build
    ```

    デプロイ先ディレクトリhtmlにファイルができていたら成功です。


## カスタマイズする各設定の説明

まず試しとしてsrcディレクトリ内の構造を以下のようにしてみます。

```bash
  └・src #作業用ディレクトリ
  　└・index.html
  　└・aboutus
    　└・index.html
  　└・js
  　  └・main.js #Viteが作成したファイル
  　  └・[使用するJavaScriptの各ファイル名など].js
  　    └・modules
    　    └・[各モジュール名].js
  　└・lib
     └・components #EJSで使用するHTMLコンポーネントファイルを格納
       └・metadata.ejs
    　└・pagedata #EJSで使用するデータ(JSON）ファイルを格納
       └・pagedata.json
  　└・public #public ディレクトリ
  　  └・assets
    　  └・images #使用する画像を入れておく
    　  └・css #コンパイルを通したくないCSSファイルを入れておく
    　  └・js #コンパイルを通したくないjsファイルを入れておく
  　└・scss #構造は任意で
    　└・css
      　└・style.scss
    　└・components
      　└・_[各コンポーネントファイル名].scss
```
publicディレクトリの中身はそのままbuild先のディレクトリにコピーされます。
詳しくはこちらを参照ください。

[参考:Vite - 静的アセットの取り扱い Publicディレクトリ](https://ja.vitejs.dev/guide/assets.html#public-%E3%83%86%E3%82%99%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA)

main.jsの内容を空にしてください。
また、```base: './'```と設定した場合は相対パスになるので、htmlからの各アセットファイルへのリンクを相対パスに修正しておきます。

* * *

### 出力ファイル名などを開発ディレクトリ（src）内のものと合わせる設定

デフォルトでは```index.[hash].js```という形での出力になるので、わかりやすく元のファイル名を使用するための設定を追加していきます。
出力ファイル名をカスタマイズする場合はrollupOptionsに設定を追加します。

```diff_javascript:vite.config.js
  build: {
    outDir: '../html',
+   rollupOptions: {
+     input: {
+        common: 'src/js/common.js',
+        home: 'src/js/home.js'
+     },
+     output: {
+         entryFileNames: `assets/js/[name].js`
+     }
```
上記のようにrollpuOptionsのinput、outputにObject形式で設定をするとファイル名を指定することができます。
[参考URL Rollup Configuration Options input ›](https://rollupjs.org/configuration-options/#input)
ただし、全てのファイルをこのような形でひとつずつ登録するのは手間となります。
そこで**Glob**を使い該当ディレクトリ内を検索して該当するファイル全てを一括で設定できるようにします。

```bash:Terminal
# Globをインストールします。
/var/www# npm install glob --save-dev
# Sassも使うのでSassもインストール
/var/www# npm install sass --save-dev
```
```vite.config.js```にimportします。
その他に```node:path```と```node:url```も使うのでimportします。

```diff_javascript:vite.config.js
import { defineConfig } from 'vite';
+ import {globSync} from 'glob';//ワイルドカードを使って各ファイルの名前を取得し一括で登録するため
+ import path from 'node:path';//上記の実行次にnpmのpathを利用
+ import { fileURLToPath } from 'node:url';//上記の実行時にURLをpathに変更させるため
```
globを使って指定のファイルタイプのファイル名を一括で取得し、配列にいれ、各ファイルタイプの配列をconcatでmergeしてからObjectに変換させるスクリプトを書きます。

```diff_javascript:vite.config.ejs
//JavaScriptファイル名を取得する設定　ignoreでnode_modules内やhtmlディレクトリ内は弾くようにしておく
+const inputJsArray = globSync('src/**/*.js', { ignore: ['node_modules/**','**/modules/**','**/html/**'] +}).map(file => {
+  return [
+    path.relative(
+      'src/js',
+      file.slice(0, file.length - path.extname(file).length)
+    ),
+    fileURLToPath(new URL(file, import.meta.url))
+  ]
+});
//HTMLファイル名を取得する設定
+const inputHtmlArray = globSync(['src/**/*.html'], { ignore: ['node_modules/**'] }).map(file => {
+  return [
+    path.relative(
+      'src',
+      file.slice(0, file.length - path.extname(file).length)
+    ),
+    fileURLToPath(new URL(file, import.meta.url))    
+  ]
+});
//SCSSファイルを取得する設定
+const inputScssArray = globSync('src/scss/**/*.scss', { ignore: ['src/scss/**/_*.scss'] }).map(file => {
+  return [
+    path.relative(
+      'src',
+      file.slice(0, file.length - path.extname(file).length)
+    ),
+    fileURLToPath(new URL(file, import.meta.url))    
+  ]
+});

/**　配列まとめてからObjectにする設定 */
+const inputObj = Object.fromEntries(inputJsArray.concat(inputHtmlArray, inputScssArray));

export default defineConfig({
//...
});
```

rollupOptionsの設定をします。

```diff_javascript:vite.config.js
//...
export default defineConfig({
  root: './src', //開発ディレクトリ設定
  base: './',
  build: {
    outDir: '../html', //出力場所の指定
+   emptyOutDir: true,//書き出すときにディレクトリを一旦空にする指定（どちらでもお好きな方で）
+    rollupOptions: {
+      input: inputObj,//Globで該当ファイル名取得してObjectにしたもの
+      output: {
+        entryFileNames: `assets/js/entry-[name].js`,//JSの出力設定
+        chunkFileNames: `assets/js/modules/[name].js`,//共通使用のModule　JSのの出力設定
+        assetFileNames: (assetInfo) => {
+          
+          if (/\.( gif|jpeg|jpg|png|svg|webp| )$/.test(assetInfo.name)) {
+            return 'assets/images/[name].[ext]';//画像アセットの出力設定
+          }
+          
+          if (/\.css$/.test(assetInfo.name)) {
+            return 'assets/css/[name].[ext]';//CSSアセットの出力設定
+          }
+         
+          return 'assets/[name].[ext]'; //その他のファイルの出力設定
+        },
+      }
    }
//..
```

:::note warn
~~上記の設定で画像ファイルやHTMLファイルはディレクトリ構造も反映して保存されますが、JSとCSSはファイル名のみ反映される形になってしまいます。~~
~~もしJSとCSSも出力先に元のディレクトリ構造を反映させたい場合はもう一工夫必要となります。~~
Vite4では[name]にはファイル名しか入らなかったですがVite5(Rollup4)からはディレクトリ名を入れて渡すと[name]にもディレクトリが反映されます。Vite4を使う場合はコメント欄に記載したような関数をかます必要がありますが、Vite5からは必要ないようです。

ただし、CSSのディレクトリ構成が元のディレクトリのままの場合、階層が深いファイルからの画像へのパスがきちんと反映されないようです。下記にある`postcss-url`などで画像すべてをbase64にしてinline化する方法では問題は出ないのですが、そのままでは画像へのパスが変わってしまいます。
その場合は、Scssのディレクトリ構成と同じにせず、`assets/css`ディレクトリ直下に全てするように設定したら上手くいきました。
:::
```diff_javascript
//SCSSファイルを取得する設定
const inputScssArray = globSync('src/scss/**/*.scss', { ignore: ['src/scss/**/_*.scss'] }).map(file => {
+  const fileName = file.slice(file.lastIndexOf('/') + 1, file.length - path.extname(file).length);
  return [
-    path.relative(
-      'src/scss',
-      file.slice(0, file.length - path.extname(file).length)
-    ),
+    fileName,
    fileURLToPath(new URL(file, import.meta.url))    
  ]
});
```
**※以下のプルリクで反映された様子**

https://github.com/vitejs/vite/pull/12773

**テストをしてみる**
```bash
/var/www# npm run build
```

htmlディレクトリに書き出されたらOKです。もしエラーが出たらその内容を確認して修正していきます。

* * *

### PostCSSの設定

以下のPostCSSプラグインを使用できるように設定します。

**1)Autoprefixer**
    定番の自動的にベンダープレフィックスを付け、必要であれば構文の変更もしてくれるツールです
   
https://github.com/postcss/autoprefixer

**2)postcss-sort-media-queries**
重複するMediaQueryをまとめて、指定に沿ってソートさせるプラグイン

https://github.com/yunusga/postcss-sort-media-queries

**3)postcss-url**
画像などのURLをから画像データを取得して、inline埋め込むをするプラグイン

https://github.com/postcss/postcss-url#readme

まず上記をインストールしていきます。

```bash:Terminal
/var/www# npm install postcss autoprefixer postcss-sort-media-queries postcss-url --save-dev

```
postcss.config.cjsに設定を書いていきます。

```javascript:postcss.config.cjs
module.exports = () => ({
    plugins: 
        [
            require('autoprefixer')({}),
            require('postcss-sort-media-queries')({
                    sort: 'mobile-first'
                }
            ),
            /*require('postcss-clip-path-polyfill')(),*/
            require('postcss-url')({
                url: 'inline',
                maxSize: 150,//インラインにする画像ファイルの容量上限
            }),
            
        ]
})

```

これでbuildテストをしてエラーが出なければOKです。

* * *

### EJSの設定
EJSを使用するにあったって今回はプラグインを使用します。
[vite-plugin-ejs](https://www.npmjs.com/package/vite-plugin-ejs)
```bash:Terminal
/var/www# npm i vite-plugin-ejs --save-dev
```

EJSに読み込ませるデータをjson形式にて設定します。

```json:./src/lib/pagedata/data.json　(例)
{
    "data": {
        "home": {
            "title" : "HOME PAGE ですよ",
            "description": "HOMEページの概要をここに記述します。\nHOMEページの概要をここに記述します。",
            "ogimage": "[HOMEのog:image用ファイル名].jpg"
        },
        "aboutus": {
            "title" : "about usですよ",
            "description": "about usページの概要をここに記述します。",
            "ogimage": "[aboutusのog:image用ファイル名].jpg"
        }
    }
}
```

※ogimage用の画像はpublic/assets/imageディレクトリに入れておきます。

vite.config.jsへ追加します。

```diff_javascript:vite.config.js
import { defineConfig } from 'vite';
import glob from 'glob';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
+ import { ViteEjsPlugin } from "vite-plugin-ejs";
+ import json from "./src/lib/pagedata/data.json";//ページデータ用のjson読み込み

export default defineConfig({
//...
},
+ plugins: [
+  ViteEjsPlugin(json),//プラグインの追加　読み込んだjsonを引数にて渡す
+ ]
```

各HTMLファイルに読み込ませる共通のComponentファイル（ejs）を作成します。


headのmeta　data用のComponent
```javascript:./src/lib/components/metadata.ejs
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="<%- data[slug].description.replace(/(\r)?\n/g,'') %>">
    <meta prottype="og:image" contnt="<%- rootDirectory %>assets/images/<%- data[slug].ogimage %>">
    //...
    //...
    <title><%= data[slug].title %></title>
```

各ページ共通のヘッダーComponent
```javascript:./src/lib/components/st-glb_pageHeader.ejs
<header class="st-glb_pageHeader">
    <h1 class="st-glb_pageHeader-title">
        <%= data[slug].title %>
    </h1>
    <!-- /.st-glb_pageHeader-title -->
    <p class="st-glb_pageHeader-description">
        <% const headerDescription = data[slug].description.replace(/(\r)?\n/g, '<br>'); %>
        <%- headerDescription; %>
    </p>
    <!-- /.st-glb_pageHeader-description -->
</header>
<!-- /.st-glb_pageHeader -->
```

::: note info
 上記は例としてheadのmetaデータ用と各ページ共通のヘッダーComponentを作成しています。
:::



トップページのHTMLファイルを作成します。
ページ情報（metaInfo）として、slug名とrootディレクトリからの相対パスを設定しておきます。
```javascript:index.html
<!DOCTYPE html>
<%_
/**
 * ページデータ設定
 */
  const metaInfo = {
    slug: "home",
    rootDirectory: "./"
  };
_%>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <%- include('lib/components/metadata',metaInfo); %>
    <link rel="stylesheet" href="<%= metaInfo.rootDirectory %>scss/firstview/fv-cmn.scss">
    <link rel="stylesheet" href="<%= metaInfo.rootDirectory %>scss/style/style.scss">
    <script type="module" defer src="<%= metaInfo.rootDirectoryt %>js/main.js"></script>
    <script type="module" defer src="<%= metaInfo.rootDirectory %>js/parallax.js"></script>
  </head>
  <body> 
    <main class="st-glb_mainContent">
      <article class="st-glb_mainContent-article">
        <%- include('lib/components/st-glb_pageHeader', metaInfo); %>
      </article>
      <!-- /.main-contents -->
    </main>
    <!-- /.l-mainContent -->
  </body>
</html>

```

EJSファイルを読み込むにはinclude()を使用します。
第２引数に読み込むEJSファイルに渡す引数（Object）を設定することができます。
上記の例のmetaInfoオブジェクトを渡すと読み込むEJSファイル側でslugやrootDirectoryなどが使えます。

### Devサーバーを起動してみる。
```bash
/var/www# npm run dev
```
エラーが出なければOKです。

### buildしてみる
```bash
# 書き出しチェック
/var/www# npm run build
```
buildすると、```/html/```ディレクトリに想定通りのファイルが作成できていたらOKです。

* * *


### 開発モードでEJSのComponetファイルを更新してもLive Reloadが動かない対処

EJSのComponentファイル（.ejs)はbuidl時に出力させたくないので、rollupOptionsの対象から外しています。
そうしますと開発モード時にこれらのファイルを更新しても上手く動作をしてくれません。
そこで、.ejsファイルを出力せずに監視対象にするようにできるvite-plugin-live-reloadを導入しました。

https://github.com/arnoson/vite-plugin-live-reload

インストールします。
```bash
/var/www# npm i vite-plugin-live-reload --save-dev
```

vite.config.jsにimportして、pluginの登録設定をします。

```diff_javascript:vite.config.js
//...
import { ViteEjsPlugin } from "vite-plugin-ejs";
import json from "./src/lib/pagedata/data.json";
+ import liveReload from 'vite-plugin-live-reload';
//...

export default defineConfig({
//...
  },
  plugins: [
+    liveReload(['lib/components/**/*.ejs']),
    ViteEjsPlugin(json),
  ]
```

テストします。
```bash:Terminal
/var/www# npm run dev
```

該当するファイルを修正/保存してLive Reloadが動けばOKです。

* * *
ざっと書いてみましたが抜けがあるかもしれませんので、その際にはご指摘お願いいたします。

# Webpackと比べてみて

開発サーバーの動作に関してはサクサク動いてストレスが全くないです。
Webpackの場合Dart Sassでのコンパイルの遅さが開発サーバーにも影響があるようで、この問題が解決しない限り高速化は無理そうです。
[参考:sass-loaderとdart-sassにまつわるfibersの話](https://blog.nasbi.jp/programming/devenv/webpack/the-story-of-fibers-about-sass-loader-and-dart-sass/)

ビルドの速度は半分になりました。
少しSassのファイル構造が深いもので比べてみたところ、Webpackではビルド時に速くても30秒強かかっていたものが、15秒以下でビルドできました。

```vite build --watch```に関して。
こちらは、Webpackのbuild --watchの方が若干速いです。
初回buildはViteの方が速いのですが、webpack の--watchの方がキャッシュの使い方が良いのかわからないのですが、若干速かったです。


# 参考にしたサイト

https://ja.vitejs.dev/

https://rollupjs.org/introduction/

https://github.com/trapcodeio/vite-plugin-ejs

https://qiita.com/miwashutaro0611/items/36910f2d784ff70a527d