WASM の Webpack を v4 から v5 に Update した

## 概要
過去に WASM で試した package.json の脆弱性のアラートが上がっていたので対応した。

## 対応結果

大まかに次のような対応を行った。
最初に CopyWebpackPlugin の非互換で webpack-dev-server が死ぬ。
そのあと、依存パッケージのバージョンの不整合(古い)で死ぬ。

* copy-webpack-plugin関連
  * 5.x → 11.x
  * 非互換の対応: webpack.config.js 
    * patterns の書き方にした
    * WASM 向けの experiments 設定を追加
* webpack-cli/webpack-dev-server関連
  * 3.x → 4.x
* ajv
  * 8.11.0 をインストール 

エラーメッセージで英語サイト探せば見つかる。
日本語サイトだと解決策は見つからない(未解決事例がトップに来たりとかよくある)。

### package.json

```diff_json
@@ -1,36 +1,38 @@
 {
   "name": "create-wasm-app",
   "version": "0.1.0",
   "description": "create an app to consume rust-generated wasm packages",
   "main": "index.js",
   "bin": {
     "create-wasm-app": ".bin/create-wasm-app.js"
   },
   "scripts": {
     "build": "webpack --config webpack.config.js",
     "start": "webpack-dev-server"
   },
   "repository": {
     "type": "git",
     "url": "git+https://github.com/rustwasm/create-wasm-app.git"
   },
   "keywords": [
     "webassembly",
     "wasm",
     "rust",
     "webpack"
   ],
   "author": "Ashley Williams <ashley666ashley@gmail.com>",
   "license": "(MIT OR Apache-2.0)",
   "bugs": {
     "url": "https://github.com/rustwasm/create-wasm-app/issues"
   },
   "homepage": "https://github.com/rustwasm/create-wasm-app#readme",
   "devDependencies": {
+    "@webpack-cli/serve": "^1.7.0",
+    "ajv": "^8.11.0",
+    "copy-webpack-plugin": "^11.0.0",
     "hello-wasm-pack": "^0.1.0",
-    "webpack": "^4.29.3",
-    "webpack-cli": "^3.1.0",
-    "webpack-dev-server": "^3.1.5",
-    "copy-webpack-plugin": "^5.0.0"
+    "webpack": "^5.74.0",
+    "webpack-cli": "^4.10.0",
+    "webpack-dev-server": "^4.10.0"
   }
 }
```

### webpack.config.js

```diff_javascript
@@ -1,14 +1,21 @@
 const CopyWebpackPlugin = require("copy-webpack-plugin");
 const path = require('path');

 module.exports = {
   entry: "./bootstrap.js",
   output: {
     path: path.resolve(__dirname, "dist"),
     filename: "bootstrap.js",
   },
+  experiments: {
+    asyncWebAssembly: true,
+  },
   mode: "development",
   plugins: [
-    new CopyWebpackPlugin(['index.html'])
+    new CopyWebpackPlugin({
+      patterns: [
+        "index.html"
+      ],
+    }),
   ],
 };
```

* [CopyWebpackPlugin | webpack](https://webpack.js.org/plugins/copy-webpack-plugin/)
  CopyWebpackPlugin index.html - Google Search
* [Experiments | webpack](https://webpack.js.org/configuration/experiments/)
  experiments.syncWebAssembly: true - Google Search

## 移行時に出力されたエラーたち

パス等は伏せています。

### Error 1: npm run start が失敗 - webpack-dev-server のせい

```
$ npm run start

> create-wasm-app@0.1.0 start $HOME/.../www
> webpack-dev-server

$HOME/.../www/node_modules/copy-webpack-plugin/node_modules/schema-utils/dist/validate.js:115
    throw new _ValidationError.default(errors, schema, configuration);
    ^

ValidationError: Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
 - options[0] should be an object:
   object { patterns, options? }
    at validate ($HOME/.../www/node_modules/copy-webpack-plugin/node_modules/schema-utils/dist/validate.js:115:11)
    at new CopyPlugin ($HOME/.../www/node_modules/copy-webpack-plugin/dist/index.js:172:5)
    at Object.<anonymous> ($HOME/.../www/webpack.config.js:12:5)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at WEBPACK_OPTIONS ($HOME/.../www/node_modules/webpack-cli/bin/utils/convert-argv.js:114:13) {
  errors: [
    {
      instancePath: '[0]',
      schemaPath: '#/type',
      keyword: 'type',
      params: { type: 'object' },
      message: 'must be object',
      schema: 'object',
      parentSchema: {
        definitions: {
          ObjectPattern: {
            type: 'object',
            additionalProperties: false,
            properties: [Object],
            required: [Array]
          },
          StringPattern: { type: 'string', minLength: 1 }
        },
        type: 'object',
        additionalProperties: false,
        properties: {
          patterns: { type: 'array', minItems: 1, items: [Object] },
          options: {
            type: 'object',
            additionalProperties: false,
            properties: [Object]
          }
        },
        required: [ 'patterns' ]
      },
      data: 'index.html'
    }
  ],
  schema: {
    definitions: {
      ObjectPattern: {
        type: 'object',
        additionalProperties: false,
        properties: {
          from: {
            type: 'string',
            description: 'Glob or path from where we copy files.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#from',
            minLength: 1
          },
          to: {
            anyOf: [Array],
            description: 'Output path.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#to'
          },
          context: {
            type: 'string',
            description: "A path that determines how to interpret the 'from' path.",
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#context'
          },
          globOptions: {
            type: 'object',
            description: 'Allows to configute the glob pattern matching library used by the plugin.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#globoptions'
          },
          filter: {
            instanceof: 'Function',
            description: 'Allows to filter copied assets.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#filter'
          },
          transformAll: {
            instanceof: 'Function',
            description: 'Allows you to modify the contents of multiple files and save the result to one file.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#transformall'
          },
          toType: {
            enum: [Array],
            description: 'Determinate what is to option - directory, file or template.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#totype'
          },
          force: {
            type: 'boolean',
            description: "Overwrites files already in 'compilation.assets' (usually added by other plugins/loaders).",
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#force'
          },
          priority: {
            type: 'number',
            description: 'Allows to specify the priority of copying files with the same destination name.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#priority'
          },
          info: {
            anyOf: [Array],
            description: 'Allows to add assets info.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#info'
          },
          transform: {
            description: 'Allows to modify the file contents.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#transform',
            anyOf: [Array]
          },
          noErrorOnMissing: {
            type: 'boolean',
            description: "Doesn't generate an error on missing file(s).",
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#noerroronmissing'
          }
        },
        required: [ 'from' ]
      },
      StringPattern: { type: 'string', minLength: 1 }
    },
    type: 'object',
    additionalProperties: false,
    properties: {
      patterns: {
        type: 'array',
        minItems: 1,
        items: { anyOf: [ [Object], [Object] ] }
      },
      options: {
        type: 'object',
        additionalProperties: false,
        properties: {
          concurrency: {
            type: 'number',
            description: 'Limits the number of simultaneous requests to fs.',
            link: 'https://github.com/webpack-contrib/copy-webpack-plugin#concurrency'
          }
        }
      }
    },
    required: [ 'patterns' ]
  },
  headerName: 'Copy Plugin',
  baseDataPath: 'options',
  postFormatter: null
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! create-wasm-app@0.1.0 start: `webpack-dev-server`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the create-wasm-app@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     $HOME/.npm/_logs/2022-08-10T18_42_45_121Z-debug.log
```

### Error 2: npm run start が失敗 - @webpack-cli/serve を入れろと怒られる

次の記事がある。

* [webpack-cli - "Would you like to install webpack-cli?(yes/NO)" crash to solve - DEV Community](https://dev.to/arisa_dev/webpack-cli-would-you-like-to-install-webpack-cli-yes-no-crash-to-solve-526l)
  TypeError: Class constructor ServeCommand cannot be invoked without 'new' - Google Search

```console
$ npm run start

> create-wasm-app@0.1.0 start $HOME/.../www
> webpack-dev-server

The command moved into a separate package: @webpack-cli/serve
Would you like to install serve? (That will run npm install -D @webpack-cli/serve) (yes/NO) :
@webpack-cli/serve needs to be installed in order to run the command.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! create-wasm-app@0.1.0 start: `webpack-dev-server`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the create-wasm-app@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     $HOME/.npm/_logs/2022-08-10T19_12_31_565Z-debug.log
```

### Error 3: npm run start が失敗

次のコマンドで webpack-cli@3 → webpack-cli@4 とすることで解消した。

```
npm install --save-dev webpack-cli@4
```

```console
$ npm run start

> create-wasm-app@0.1.0 start $HOME/.../www
> webpack-dev-server

$HOME/.../www/node_modules/webpack-cli/bin/utils/prompt-command.js:46
        return func(...args);
               ^

TypeError: Class constructor ServeCommand cannot be invoked without 'new'
    at runWhenInstalled ($HOME/.../www/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9)
    at promptForInstallation ($HOME/.../www/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10)
    at $HOME/.../www/node_modules/webpack-cli/bin/cli.js:32:43
    at Object.<anonymous> ($HOME/.../www/node_modules/webpack-cli/bin/cli.js:366:3)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! create-wasm-app@0.1.0 start: `webpack-dev-server`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the create-wasm-app@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     $HOME/.npm/_logs/2022-08-10T19_18_32_026Z-debug.log
```

### Error 4: npm run start が失敗 - ajv がない

`npm install --save-dev ajv@latest` をインストール。

* [Error: Cannot find module 'ajv/dist/compile/codegen' · Issue #875 · webpack-contrib/mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin/issues/875)


```console
$ npm run start

> create-wasm-app@0.1.0 start $HOME/.../www
> webpack-dev-server

[webpack-cli] You need to install 'webpack-dev-server' for running 'webpack serve'.
Error: Cannot find module 'ajv/dist/compile/codegen'
Require stack:
- $HOME/.../www/node_modules/ajv-keywords/dist/definitions/typeof.js
- $HOME/.../www/node_modules/ajv-keywords/dist/keywords/typeof.js
- $HOME/.../www/node_modules/ajv-keywords/dist/keywords/index.js
- $HOME/.../www/node_modules/ajv-keywords/dist/index.js
- $HOME/.../www/node_modules/schema-utils/dist/validate.js
- $HOME/.../www/node_modules/schema-utils/dist/index.js
- $HOME/.../www/node_modules/webpack-dev-server/lib/Server.js
- $HOME/.../www/node_modules/@webpack-cli/serve/lib/index.js
- $HOME/.../www/node_modules/webpack-cli/lib/webpack-cli.js
- $HOME/.../www/node_modules/webpack-cli/lib/bootstrap.js
- $HOME/.../www/node_modules/webpack-cli/bin/cli.js
- $HOME/.../www/node_modules/webpack-dev-server/bin/webpack-dev-server.js
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! create-wasm-app@0.1.0 start: `webpack-dev-server`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the create-wasm-app@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     $HOME/.npm/_logs/2022-08-10T19_22_37_926Z-debug.log
```