[Angular] Angular 12 (Webpack 5)アップデート記念ビルドエラー対処メモ

Angular 12から、Webpackが5になりました。

そのせいではないと思うのですが、ビルド時にいくつかエラーになりました。

これはその対処メモです。

# 環境

* Angular 12.0.4
* [pdfjs](https://www.npmjs.com/package/pdfjs-dist) 2.7.570
* [pdfAnnotate](https://www.npmjs.com/package/annotpdf) 1.0.14
* Node 14.17.0


# Warning: Module not found: Error: Can't resolve 'crypto'

## 起こったこと

`ng serve`時にこのようなメッセージが出力されました。


```console
./node_modules/crypto-js/core.js:43:22-39 - Warning: Module not found: Error: Can't resolve 'crypto' in '~/node_modules/crypto-js'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
        - add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
        - install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
        resolve.fallback: { "crypto": false }
```


## 解決策

crypto-browserifyパッケージをインストールし、`tsconfig.json`ファイルを編集しました。

```console
npm i crypto-browserify
```



```diff
{
  "compilerOptions": {
  ...
+    "paths": {
+      "crypto": ["./node_modules/crypto-browserify"]
```

# Warning: Module not found: Error: Can't resolve 'stream'

## 起こったこと

先の`crypto`と同内容なので省略します。

## 解決策

stream-browserifyパッケージをインストールし、`tsconfig.json`ファイルを編集しました。

```console
npm i stream-browserify
```

```diff
{
  "compilerOptions": {
  ...
+    "paths": {
+      "stream": ["./node_modules/stream-browserify"]
```

# Warning: CommonJS or AMD dependencies can cause optimization bailouts.

## 起こったこと

`ng serve`時にこのようなメッセージが出力されました。

```console
Warning: ~/src/app/app.component.ts depends on 'annotpdf'. CommonJS or AMD dependencies can cause optimization bailouts.
For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies
```

## 解決策（？）

https://angular.io/guide/build#configuring-commonjs-dependencies によると、CommonJSモジュールを使うのは避けたほうがよいそうですが、使う場合は `angular.json`ファイルを編集します。

```diff
   "projects": {
     "project-name": {
       ...
       "architect": {
         "build": {
           ...
           "options": {
+            "allowedCommonJsDependencies": [
+              "annotpdf"
+            ]
``` 




# Error: Module not found: Error: Can't resolve 'fs'

## 起こったこと

`ng serve`時にこのようなメッセージが出力されました。


```console
./node_modules/annotpdf/lib/annotation.js:121:25-38 - Error: Module not found: Error: Can't resolve 'fs' in '~/node_modules/annotpdf/lib'
```

## 解決（しなかった）策

`package.json`ファイルを編集しました。

```diff
+  "browser": {
+    "fs": false
+  }
```

これでよいと聞いたんですけれど、解決しないです。
なんででしょ？

## Webpackに設定追加すればよいらしい（？）

検索すると、Webpackの設定に下記を加えれば解決する（？）という話がありました。

```json
resolve: {
  fallback: {
    fs: false,
  },
},
```

Webpackは、[@angular-devkit/build-angularのこのあたり](https://github.com/angular/angular-cli/blob/444ca3aad38e940a7c72b593e81036dfa578e7af/packages/angular_devkit/build_angular/src/browser/index.ts#L259)で実行しているようです。

Webpackの設定箇所を探してみると、[このへん](https://github.com/angular/angular-cli/blob/f2ce053885a718f7a040e2ff66deef1fe9042cea/packages/angular_devkit/build_angular/src/browser/index.ts#L130)で下記の呼び出し結果をまとめている様子です。

* [getCommonConfig](https://github.com/angular/angular-cli/blob/444ca3aad38e940a7c72b593e81036dfa578e7af/packages/angular_devkit/build_angular/src/webpack/configs/common.ts#L50)
* [getBrowserConfig](https://github.com/angular/angular-cli/blob/a234c66a4faca512604d69816055b142e28296f1/packages/angular_devkit/build_angular/src/webpack/configs/browser.ts#L16)
* [getStylesConfig](https://github.com/angular/angular-cli/blob/a234c66a4faca512604d69816055b142e28296f1/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts#L454)
* [getStatsConfig](https://github.com/angular/angular-cli/blob/a234c66a4faca512604d69816055b142e28296f1/packages/angular_devkit/build_angular/src/webpack/configs/stats.ts#L53)
* [getAnalyticsConfig](https://github.com/angular/angular-cli/blob/a234c66a4faca512604d69816055b142e28296f1/packages/angular_devkit/build_angular/src/browser/index.ts#L107)
* [getCompilerConfig](https://github.com/angular/angular-cli/blob/a234c66a4faca512604d69816055b142e28296f1/packages/angular_devkit/build_angular/src/browser/index.ts#L122)
* [getTypeScriptConfig](https://github.com/angular/angular-cli/blob/a234c66a4faca512604d69816055b142e28296f1/packages/angular_devkit/build_angular/src/webpack/configs/typescript.ts#L81)


上記をまとめた結果はこんなんです。

```json
resolve: {
  roots: [projectRoot],
  extensions: ['.ts', '.tsx', '.mjs', '.js'],
  symlinks: !buildOptions.preserveSymlinks,
  modules: [wco.tsConfig.options.baseUrl || projectRoot, 'node_modules'],
  mainFields: ['es2015', 'browser', 'module', 'main'],
},
```

`fallback`を差し込む余地はなさそうです。


[@angular-devkit/build-webpack](https://www.npmjs.com/package/@angular-devkit/build-webpack)を使えばよいのですが、自前でWebpackの設定を用意するのはしんどそう。。。





## エラー出力箇所を追ってみる

よくわからないので、ソースを追ってみます。

エラーメッセージを出力しているのは、[WebpackのCompilation.js](https://github.com/webpack/webpack/blob/487665cb69f7b1f1aac44097124ef8f33cfe247b/lib/Compilation.js#L1762)のようです。

出力している`ModuleNotFoundError`に渡る**Can't resolve 'fs'**は、[resolveMatchedConfigs](https://github.com/webpack/webpack/blob/487665cb69f7b1f1aac44097124ef8f33cfe247b/lib/sharing/resolveMatchedConfigs.js#L61)かな。
参照リゾルバが解決できないとエラーになる様子です。


参照リゾルバは、[ResolverFactory](https://github.com/webpack/webpack/blob/5dd661cc39c1c59c70859afa55757cdb483418cc/lib/ResolverFactory.js#L72)が[作っていました](https://github.com/webpack/webpack/blob/5dd661cc39c1c59c70859afa55757cdb483418cc/lib/ResolverFactory.js#L123)。実際には[enhanced-resolve](https://www.npmjs.com/package/enhanced-resolve)に[下請け](https://github.com/webpack/enhanced-resolve/blob/60d79f3c93304ce5ecbbe0127aa583d4a73bf1a1/lib/ResolverFactory.js#L241)させてます。

enhanced-resolveのドキュメントによると[Node.jsの解決ルール](https://nodejs.org/docs/latest-v14.x/api/modules.html#modules_all_together)に従って、解決してくれるそうです。**fs**はコアモジュールなので、そのままスルーっぽいですね。


## 解決策

[@angular-builders/custom-webpack](https://www.npmjs.com/package/@angular-builders/custom-webpack)というよいものがありました。これを使うと、Angularデフォルトのwebpack設定を上書きできるようです。

インストールします。

```console
npm i -D @angular-builders/custom-webpack
```

`angular.json`を書き換えます。

```diff
       "architect": {
         "build": {
-          "builder": "@angular-devkit/build-angular:browser",
+          "builder": "@angular-builders/custom-webpack:browser",
           "options": {
+            "customWebpackConfig": {
+              "path": "webpack.config.ts"
+            },
```

```diff
         "serve": {
-          "builder": "@angular-devkit/build-angular:dev-server",
+          "builder": "@angular-builders/custom-webpack:dev-server",
           "options": {
             "browserTarget": "ex-pdf-annotate:build"
           },
```

`webpack.config.ts`ファイルを新規作成します。

```ts
import { Configuration } from 'webpack';

export default {
  resolve: {
    fallback: {
      fs: false,
    },
  },
} as Configuration;
```

これで`ng build`と`ng serve`がエラーなくできるようになりました。




# 参考リンク

* https://github.com/ghiscoding/Angular-Slickgrid/issues/769#issuecomment-849573463