GitLabのHandbookがWSL2でプレビューが正しく動作しない

# 現象

有志でオープンソースで公開されているGitLabさんのHandbookを翻訳して日本語版立ち上げていますが。

こちらの `https://gitlab.com/gitlab-com/www-gitlab-com/` からフォークして、ハンドブックのプレビューを起動すると

```sh
alias serve_handbook_faster="cd /home/workspaces/jp-www-gitlab-com/sites/handbook; NO_CONTRACTS=true bundle exec middleman serve" 

serve_handbook_faster
```

と、ページ上部にエラーがでます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302078/f9aa505f-ee86-caf1-8491-69558f93105c.png)

# 調査

## インポートできていない？
該当箇所
```scss:source/stylesheets/be-navigation.css.scss
@import "~be-navigation/dist/be-navigation";
:
```

チルダ `~` は `node_modules`配下をみるエイリアスです

```js:webpack.config.js
  resolve: {
    modules: [
      "node_modules"
    ]
  },
```

`webpack.config.js` に `resolve.modules` が定義されているので問題ないようです

```sh
$ ls node_modules/be-navigation/dist/
be-navigation.css  be-navigation.umd.min.js
```

ちなみに `be-navigation` は、Navigation for https://about.gitlab.com です

https://www.npmjs.com/package/be-navigation

## webpackの動作をチェック

```json:package.json
  "scripts": {
    "start-webpack": "NODE_ENV=development npx webpack serve",
    "build-webpack": "NODE_ENV=production npx webpack --bail --progress",
```

```sh
$ NODE_ENV=development npx webpack serve
ℹ ｢wds｣: Project is running at http://0.0.0.0:4567/
ℹ ｢wds｣: webpack output is served from /
ℹ ｢wds｣: Content not from webpack is served from /home/workspaces/jp-www-gitlab-com
ℹ ｢wdm｣: assets by path javascripts/ 109 MiB
  assets by path javascripts/*.js 106 MiB 276 assets
  assets by path javascripts/libs/*.js 2.25 MiB 37 assets
  assets by path javascripts/applications/*.js 1.08 MiB
    asset javascripts/applications/search_bar.js 372 KiB [emitted] (name: applications/search_bar)
    asset javascripts/applications/broken_image_placeholder.js 366 KiB [emitted] (name: applications/broken_image_placeholder)
    asset javascripts/applications/categories_scroller.js 366 KiB [emitted] (name: applications/categories_scroller)
assets by path stylesheets/*.css 3.54 MiB 188 assets
runtime modules 234 KiB 1248 modules
orphan modules 443 bytes [orphan] 1 module
code generated modules 3.43 MiB (javascript) 3.54 MiB (css/mini-extract) [code generated]
  modules by path ./source/ 1.11 MiB (javascript) 3.54 MiB (css/mini-extract)
    cacheable modules 1.11 MiB
      modules by path ./source/stylesheets/*.scss 809 KiB 377 modules
      modules by path ./source/javascripts/ 260 KiB 85 modules
      modules by path ./source/frontend/ 72.2 KiB 41 modules
    css modules 3.54 MiB
      modules by path ./source/stylesheets/*.scss 3.02 MiB 188 modules
      modules by path ./source/stylesheets/*.scss (1) 525 KiB
        css ./node_modules/css-loader/dist/cjs.js??clonedRuleSet-1[0].rules[0].use[2]!./node_modules/sass-loader/dist/cjs.js!./source/stylesheets/2022-blog-refresh.css.scss (1) 6.43 KiB [code generated]
        css ./node_modules/css-loader/dist/cjs.js??clonedRuleSet-1[0].rules[0].use[2]!./node_modules/sass-loader/dist/cjs.js!./source/stylesheets/be-navigation.css.scss (1) 518 KiB [code generated]
  modules by path ./node_modules/ 2.31 MiB 214 modules
  external "jQuery" 42 bytes [built] [code generated]
webpack 5.16.0 compiled successfully in 10128 ms
ℹ ｢wdm｣: Compiled successfully.
```

`http://127.0.0.1:4567/stylesheets/be-navigation.css` にアクセスすると@importは解決されて正しく scss は css にコンパイルされていました

しかし `serve_handbook_faster` で middleman経由で `http://127.0.0.1:7654/stylesheets/be-navigation.css`にアクセスすると

```css
/*
SassC::SyntaxError: Error: File to import not found or unreadable: ~be-navigation/dist/be-navigation.
        on line 1:1 of ../../source/stylesheets/be-navigation.css.scss
>> @import "~be-navigation/dist/be-navigation";

   ^


Backtrace:
/home/workspaces/jp-www-gitlab-com/source/stylesheets/be-navigation.css.scss:1
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/sassc-2.4.0/lib/sassc/engine.rb:50:in `render'
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/middleman-core-4.4.2/lib/middleman-core/renderers/sass.rb:55:in `evaluate'
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/tilt-2.0.10/lib/tilt/template.rb:109:in `render'
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/middleman-core-4.4.2/lib/middleman-core/file_renderer.rb:79:in `render'
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/middleman-core-4.4.2/lib/middleman-core/template_renderer.rb:184:in `_render_with_all_renderers'
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/middleman-core-4.4.2/lib/middleman-core/template_renderer.rb:147:in `block in render'
:
/home/workspaces/jp-www-gitlab-com/vendor/bundle/ruby/3.0.0/gems/webrick-1.7.0/lib/webrick/server.rb:310:in `block in start_thread'
*/
body:before {
  white-space: pre;
  font-family: monospace;
  content: "SassC::SyntaxError: Error: File to import not found or unreadable: ~be-navigation/dist/be-navigation.\A         on line 1:1 of ../../source/stylesheets/be-navigation.css.scss\A >> @import \"~be-navigation/dist/be-navigation\";\A \A    ^\A "; }
```

middlemanが正しく動いていない可能性が見えてきました

## middlemanの調査

```ruby:sites/handbook/config.rb
:
  # External Pipeline
  unless ENV['SKIP_EXTERNAL_PIPELINE']
    # NOTE: if we use the external pipeline, we set the Middleman port to 7654,
    # which the webpack devServer config relies on in webpack.config.js in the project root.
    # We set up webpack devServer on port 4567 to keep the local dev commands the same,
    # but it proxies to 7654 for everything other than the webpack assets.
    # See doc/webpack.md for more information.
    set(:port, 7654)
    # This Middleman extension logs a custom message about the Webpack proxy port
    activate :proxy_server_information
    # NOTE: This only applies to 'development' mode.  For local builds, use the `rake build:*` tasks
    # NOTE: This runs webpack and makes assets available during local development
    activate :external_pipeline,
             name: :webpack,
             command: "cd #{monorepo_root} && yarn run start-webpack",
             source: "#{monorepo_root}/tmp/dist",
             latency: 3
  end
:
```

middleman 経由でプレビューを起動した場合(`serve_handbook_faster`)は `webpack`が動いていない？という検証をする。

`http://127.0.0.1:4567/stylesheets/be-navigation.css`にアクセスすると正しく動作

`http://127.0.0.1:7654/stylesheets/be-navigation.css`にアクセスすると正しく動作しない

という感じです

middlemanの機能やconfig.rbを確認する限り、middlemanにはwebpackへプロキシーする機能はないようです

## 起動時のメッセージの確認

英語
```text
== Webpack proxy server enabled: use "http://localhost:4567" or "http://127.0.0.1:4567"
== Please ignore the following two lines - they will be missing necessary assets.
== View your site at "http://localhost:7654", "http://127.0.0.1:7654"
== Inspect your site configuration at "http://localhost:7654/__middleman", "http://127.0.0.1:7654/__middleman"
```

日本語訳
```text
== Webpackのプロキシサーバーを有効にしました: "http://localhost:4567" or "http://127.0.0.1:4567" を使ってください
== 下記二行は無視してください - 下記では必要なアセットにアクセスできません
== サイトの閲覧はこちらから "http://localhost:7654", "http://127.0.0.1:7654"
== サイトの設定はこちらから "http://localhost:7654/__middleman", "http://127.0.0.1:7654/__middleman"
```

## さらに webpack.md の確認

docs/webpack.md というドキュメントを確認しました

以下翻訳文

````markdown
### DevServerとMiddlemanの使用

[Middleman external pipeline](https://middlemanapp.com/advanced/external-pipeline/) を使うと、WebpackをMiddlemanのビルドステップに統合することができます。これは Middleman の個々の設定ファイルで行われます。

* `sites/uncategorized/config.rb`
* `sites/handbook/config.rb`

デフォルトでは、前記のように外部パイプラインを追加すると、Webpack の出力を Middleman [サイトマップ](https://middlemanapp.com/advanced/sitemap/)に追加します。 書き出し先のディレクトリを指定する必要があります。Webpack のバンドルが変わると、Middlemanサーバがサイト全体を再構築して、Webpack も再構築する設定にしていると開発モードでは不要であり、時間もかかります。

さらに、Middleman は外部パイプラインをディスク上のディレクトリに向ける必要があるので、[Watch モード](https://webpack.js.org/guides/development/#using-watch-mode)でしか動作しません。実際にディスクに書き込むからですが、 devServer はそうではありません。

Middleman は `source/javascripts` と `source/stylesheets` ディレクトリを暗黙のうちにサイトマップに追加していることも、このプロセスを難しくしています。そのため、そこに変更を加えると、Webpack の再構築と同様に再構築のトリガーとなります（したがって、Webpack の出力がもう一度変わるのを見ながら Middleman を 3 回目の再構築を行います）。

つまり、Webpackで処理されるファイルについては、 **webpack、そしてwebpackだけ** に処理させ、Middlemanにはコードベース内のURLへの参照以外、何も知らせないようにしたいのです。

そこで、Middleman に `source/javascripts` と `source/stylesheets` ディレクトリのサイトマップの暗黙構築を無視するように設定することにします。

```rb
# sites/handbook/config.rb, sites/uncategorized/config.rb, any other config
# ...
ignore 'javascripts/*'
ignore 'stylesheets/*'
# ...
```

そして、外部パイプラインがwebpackを使うように設定し、`/tmp/dist`を指定します。この場所は、ウォッチモードを使う場合、webpackがファイルを書き込む**はずの**場所ですが、devServerが処理する間は静止していなければなりません。

```rb
# ...
activate :external_pipeline,
    name: :webpack,
    command: "cd #{monorepo_root} && yarn run start-webpack",
    source: "#{monorepo_root}/tmp/dist",
    latency: 3
# ...
```

この段階では、devServer はすべての webpack ファイルを管理しており、Middleman はそれらについて何も知りません。しかしそのため、Middleman のサーバプロセスからインメモリで **見えません**。

devServer と Middleman サーバーを同じポートにバインドすることはできませんし、アセットがリポジトリに分散しているため、開発モード用に `webpack-server-address:webpack-port/javascripts` と `webpack-server-address:webpack-port/stylesheets` などにポイントすることは広範囲かつ難しい変更でしょう。もし、参照を整理して、Webpacksの出力をすべてバンドルすれば、そのようなことができるようになります。

しかし、開発環境を同じにするために、[devServer proxy](https://webpack.js.org/configuration/dev-server/#devserverproxy) を使い、devServer を通して Middleman に *全てを* プロキシします、こんな感じです。
 ```js
devServer: {
    index: '',
    port: 4567, 
    proxy: {
        '/': 'http://localhost:7654'
    }
}
```
````

# 結論

MiddlemanからWebpackを起動するが、MiddlemanからWebpackがコンパイルしたアセットにアクセス（Proxy）するのは難しいため、アクセスする際は、Webpack側のdevServerからアクセスしてMiddlemanへアクセスするようにということでした