11ty/eleventy で CSS / SCSS を扱うために

## はじめに

[11ty/eleventy](https://www.11ty.dev/)（以下、11ty）は **Zach Leatherman** 氏により制作された静的サイトジェネレーターです。

過去二本の記事で、11ty 公式スタートガイドによるサンプル作成と、公式サンプルプロジェクトを使用した機能確認を行いました。

- 公式スタートガイドによるサンプル作成
[HTML テンプレートエンジン詰め合わせ 11ty/eleventy を使ってみる](https://qiita.com/yuzuru_xa/items/5a55b28707501181ed6f)

- 公式サンプルプロジェクトを使用した機能確認
[静的サイトジェネレーター 11ty/eleventy をもっと使ってみる](https://qiita.com/yuzuru_xa/items/c2148a9ce31f852d8c9a)

上記の記事でも述べているように、
**11ty には CSS や JS ファイル周りのコンパイル/トランスパイルは含まれていないため、この点に関しては自身で環境構築する必要があります。**

当然ですが、CSS ファイルを用意し、HTML からスタイルシートとして読み込む方法では問題なく使用することができます。

SCSS を使用したい場合、独自で環境構築を行う必要が出てきます。
これに関して、公式ドキュメントを読みつつ色々な方法を試しましたが、どうにも狙った挙動にならない部分がありました。

本記事では 11ty プロジェクトにおいて、どう CSS / SCSS ファイルを扱っていくか、という内容について記述します。


## ディレクトリ構成およびサンプルリポジトリについて

- 入力ディレクトリ: `src/`
- 出力ディレクトリ: `dist/` 

と設定しておきます。

入出力ディレクトリの設定方法については [前回記事](https://qiita.com/yuzuru_xa/items/c2148a9ce31f852d8c9a) をご参照ください。

サンプルファイルは以下リポジトリから参照可能です。
feature/ ブランチ以下でサンプルを作り分けているので、こちらも参考になれば。

[GitHub/din65040](https://github.com/din65040/200418_11ty_scss_sample)

## CSS を HTML 内に記述する

SCSS を使用しない場合、11ty の機能として **HTML ファイル内に CSS ファイルをインクルードする**ことが可能です。
CSS を別ファイルで読み込む必要がなくなるので、ページの高速化が期待できます。


この方法を使用する場合、`_include/` 内に CSS ファイルを配置し、HTML 側からインクルードします。
11ty では、include で拡張子まで指定することが可能ですので、以下のように記述することができます。

```html:index.njk
<!doctype html>
<html>
  <head>
    <title>Page title</title>
    <style>
    {% include "style.css" %}
    </style>
  </head>
  <body>
    <h1 class="title">Cat is Justice</h1>
  </body>
</html>
```

```css:style.css
.title {
  color: red;
}
```

このファイルは、以下のようにコンパイルされます。

```html:index.html
<!doctype html>
<html>
  <head>
    <title>Page title</title>
    <style>
    .title {
  color: red;
}
    </style>
  </head>
  <body>
    <h1 class="title">Cat is Justice</h1>
  </body>
</html>
```


## npm scripts で SCSS ファイルをコンパイルする

SCSS ファイルを使用するにあたってまず考えたのは、`npm scripts` によるコンパイルです。
タスクランナー依存もなく、シンプルでよく使われる手法かと思います。

SCSS ファイルのコンパイルは `node-sass` で行います。
以下パッケージを開発環境依存として追加します。

```console:terminal
$ npm install node-sass --save-dev
$ npm install watch --save-dev
```

また、package.json のスクリプトに以下の記述を追加しておきます。

```js:package.json
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "serve": "eleventy --serve",
  "css/clean": "rimraf dist/css && mkdir -p dist/css",
  "css/scss": "node-sass src/scss/css/**.scss -o dist/css",
  "css": "npm run css/clean && npm run css/scss",
  "watch/html": "eleventy --serve",
  "watch/css": "watch 'npm run css' ./src/scss",
  "watch": "npm run watch/html & npm run watch/css"
},
```

これで `$ npm run watch` を実行すると、eleventy と SCSS のコンパイルタスクが並列で実行されます。

しかし上記の方法で作成したところ、**SCSS のコンパイル時にブラウザに自動でリロードが走らない**、という挙動となります。

### 11ty に独自のウォッチターゲットを追加する

[公式ドキュメント](https://www.11ty.dev/docs/config/)によると、.eleventy.js に以下のように記述を行うことでウォッチターゲットを追加できるとのことで、こちらの方法で改善を試みました。

```js
module.exports = function(eleventyConfig) {
  eleventyConfig.addWatchTarget("./src/scss/");
};
```

これでうまくいくのかと思いきや、SCSS のコンパイル時にリロードが走るのが 2 回に 1 回という挙動をしており、これも少しストレスです。
**（解決策をご存知の方、ご教授ください！）**

~~そういったこともあり、11ty + npm scripts での環境構築は一旦保留となりました。~~

**2020.5.2
Twitter で [@PRiMENON] (https://twitter.com/PRiMENON?s=20) さんに npm scripts での実装方法のアイデアをいただきました。ありがとうございます！**

[PRiMENON / simplety](https://github.com/PRiMENON/simplety)

コンパイル後の CSS ファイルを一度 _src 内に出力し、11ty の機能である **addPassthroughCopy** によって _site （本記事での dist ディレクトリにあたります）にコピーする、というアイデアで解決されたようです。

```.eleventy.js
  //_src/cssにファイルが更新されたら、_site/cssにコピーします
  eleventyConfig.addPassthroughCopy({"_src/css": "css"});
```

## 非公式のプラグイン eleventy-plugin-sass を使用する

11ty で SCSS のコンパイル & リロードを問題なく行うには、非公式のプラグインである [eleventy-plugin-sass](https://www.npmjs.com/package/eleventy-plugin-sass) を使用するのが今のところは良さそうです。
こちらの導入方法について記述していきます。

まずはプラグインをインストールします。
（npm のページでは `--save` となっていますが、開発環境依存では？ と思ったため `--save-dev` としています。）

```console:terminal
$ npm install eleventy-plugin-sass --save-dev
```

.eleventy.js ファイルに以下の記述を追加します。

```js:.eleventy.js
const pluginSass = require("eleventy-plugin-sass");

module.exports = function(eleventyConfig) {
  eleventyConfig.addPlugin(pluginSass, sassPluginOptions);
};
```

さらに第二引数のオプションを設定することで、 soucemap や autoprefixer の設定を行うこともできるようです。

| Key | Type | Default | description |
|:--|:--|:--|:--|
| watch | glob or array of globs | ['\*\*/\*.{scss,sass}', '!node_modules/\*\*'] | The sass files you wish to compile (and watch when you serve) |
| sourcemaps | Boolean | false | Add sourcemaps next to your sass files |
| cleanCSS | Boolean | true | Runs the css trough cleanCSS |
| cleanCSSOptions | Object | N/A | Options to pass to cleanCSS |
| autoprefixer | Boolean | true |


### コンパイル後のディレクトリ構成を変更する

こちらのプラグインを使用して SCSS ファイルのコンパイルを行う場合、プロジェクトのディレクトリ構造によっては **npm ページに記載されている方法では CSS のファイルパスが思うように設定できない**という問題が発生します。

特に入出力のディレクトリを分けている場合など、以下のような挙動となります。

入力ファイルの配置: `src/css/style.scss`
配置したい出力パス:  `dist/css/style.css`  
実際の出力:  `dist/src/css/style.css` 


これに関しては以下のように設定を記述することで、出力ディレクトリを変更することができます。

```js:.eleventy.js
const pluginSass = require("eleventy-plugin-sass");

module.exports = function(eleventyConfig) {
  eleventyConfig.addPlugin(pluginSass,{
    watch: ['src/**/*.{scss,sass}', '!node_modules/**'] // ファイルパスをこのように変更
  });

  return {
    dir: {
      input: 'src/',
      output: 'dist',
    }
  }
}
```

## まとめ

ぱっと 11ty で SCSS 環境を構築するには、`eleventy-plugin-sass` を使用するのが一番手っ取り早そうです。
npm scripts での環境構築を完結させられなかったのはまだ気になっているところですので、後々狙った挙動に修正することができれば、、と思っています。