Browse Source

Merge pull request #6339 from weseek/support/ci-for-prod

support: CI for prod
Yuki Takei 3 years ago
parent
commit
b0b6b0bdab
46 changed files with 370 additions and 159 deletions
  1. 10 0
      .eslintrc.js
  2. 37 33
      .github/workflows/ci-app-prod.yml
  3. 5 4
      .github/workflows/ci-app.yml
  4. 6 1
      .github/workflows/reusable-app-prod.yml
  5. 4 0
      packages/app/.eslintignore
  6. 11 1
      packages/app/.eslintrc.js
  7. 4 1
      packages/app/.gitignore
  8. 0 0
      packages/app/config/next-i18next.config.ts
  9. 1 0
      packages/app/docker/Dockerfile
  10. 91 70
      packages/app/next.config.js
  11. 9 8
      packages/app/package.json
  12. 0 0
      packages/app/src-obsolete/client/admin.jsx
  13. 0 0
      packages/app/src-obsolete/client/app.jsx
  14. 0 0
      packages/app/src-obsolete/client/base.jsx
  15. 0 0
      packages/app/src-obsolete/client/installer.jsx
  16. 0 0
      packages/app/src-obsolete/client/nologin.jsx
  17. 1 1
      packages/app/src/client/util/apiv3-client.ts
  18. 1 1
      packages/app/src/client/util/i18n.js
  19. 2 1
      packages/app/src/components/Admin/App/AppSetting.jsx
  20. 1 1
      packages/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx
  21. 1 1
      packages/app/src/components/Admin/Customize/CustomizeLogoSetting.tsx
  22. 6 4
      packages/app/src/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx
  23. 2 1
      packages/app/src/pages/_app.page.tsx
  24. 2 1
      packages/app/src/pages/utils/commons.ts
  25. 2 1
      packages/app/src/server/crowi/dev.js
  26. 2 1
      packages/app/src/server/crowi/express-init.js
  27. 2 1
      packages/app/src/server/models/user.js
  28. 2 1
      packages/app/src/server/routes/apiv3/app-settings.js
  29. 2 1
      packages/app/src/server/routes/apiv3/personal-setting.js
  30. 1 1
      packages/app/src/utils/array-utils.ts
  31. 14 11
      packages/app/src/utils/next.config.utils.ts
  32. 1 1
      packages/app/tsconfig.base.json
  33. 1 3
      packages/app/tsconfig.build.client.json
  34. 17 0
      packages/app/tsconfig.build.next.config.json
  35. 1 0
      packages/codemirror-textlint/package.json
  36. 1 0
      packages/core/package.json
  37. 3 2
      packages/plugin-attachment-refs/package.json
  38. 3 2
      packages/plugin-lsx/package.json
  39. 1 0
      packages/plugin-pukiwiki-like-linker/package.json
  40. 1 0
      packages/slack/package.json
  41. 1 0
      packages/slackbot-proxy/package.json
  42. 1 0
      packages/ui/.gitignore
  43. 9 1
      packages/ui/package.json
  44. 18 0
      packages/ui/tsconfig.build.cjs.json
  45. 16 0
      packages/ui/tsconfig.build.esm.json
  46. 77 4
      yarn.lock

+ 10 - 0
.eslintrc.js

@@ -56,6 +56,7 @@ module.exports = {
       },
     ],
     '@typescript-eslint/no-explicit-any': 'off',
+    '@typescript-eslint/explicit-module-boundary-types': 'off',
     indent: [
       'error',
       2,
@@ -80,4 +81,13 @@ module.exports = {
       },
     ]],
   },
+  overrides: [
+    {
+      // enable the rule specifically for TypeScript files
+      files: ['*.ts', '*.tsx'],
+      rules: {
+        '@typescript-eslint/explicit-module-boundary-types': ['error'],
+      },
+    },
+  ],
 };

+ 37 - 33
.github/workflows/ci-app-prod.yml

@@ -3,7 +3,24 @@ name: Node CI for app production
 on:
   push:
     branches:
-      - master
+      # - master
+      - support/ci-for-prod
+    paths:
+      - .github/workflows/ci-app-prod.yml
+      - .github/workflows/reusable-app-prod.yml
+      - .github/workflows/reusable-app-reg-suit.yml
+      - tsconfig.base.json
+      - yarn.lock
+      - packages/app/**
+      - '!packages/app/docker/**'
+      - packages/core/**
+      - packages/slack/**
+      - packages/ui/**
+      - packages/plugin-**
+  pull_request:
+    branches:
+        - master
+    types: [opened, reopened, synchronize]
     paths:
       - .github/workflows/ci-app-prod.yml
       - .github/workflows/reusable-app-prod.yml
@@ -16,27 +33,12 @@ on:
       - packages/slack/**
       - packages/ui/**
       - packages/plugin-**
-  # pull_request:
-  #   branches:
-  #       - master
-  #   types: [opened, reopened, synchronize]
-  #   paths:
-  #     - .github/workflows/ci-app-prod.yml
-  #     - .github/workflows/reusable-app-prod.yml
-  #     - .github/workflows/reusable-app-reg-suit.yml
-  #     - tsconfig.base.json
-  #     - yarn.lock
-  #     - packages/app/**
-  #     - '!packages/app/docker/**'
-  #     - packages/core/**
-  #     - packages/slack/**
-  #     - packages/ui/**
-  #     - packages/plugin-**
 
 jobs:
 
   test-prod-node14:
-    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    # uses: weseek/growi/.github/workflows/reusable-app-prod.yml@support/master
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@support/ci-for-prod
     with:
       node-version: 14.x
       skip-cypress: true
@@ -45,28 +47,30 @@ jobs:
 
 
   test-prod-node16:
-    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    # uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@support/ci-for-prod
     with:
       node-version: 16.x
-      skip-cypress: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
+      # skip-cypress: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
+      skip-cypress: true
       cypress-report-artifact-name: Cypress report
     secrets:
       SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
-  run-reg-suit-node16:
-    needs: [test-prod-node16]
+  # run-reg-suit-node16:
+  #   needs: [test-prod-node16]
 
-    uses: weseek/growi/.github/workflows/reusable-app-reg-suit.yml@master
+  #   uses: weseek/growi/.github/workflows/reusable-app-reg-suit.yml@master
 
-    if: always()
+  #   if: always()
 
-    with:
-      node-version: 16.x
-      skip-reg-suit: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
-      cypress-report-artifact-name: Cypress report
-    secrets:
-      REG_NOTIFY_GITHUB_PLUGIN_CLIENTID: ${{ secrets.REG_NOTIFY_GITHUB_PLUGIN_CLIENTID }}
-      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
-      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
-      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
+  #   with:
+  #     node-version: 16.x
+  #     skip-reg-suit: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
+  #     cypress-report-artifact-name: Cypress report
+  #   secrets:
+  #     REG_NOTIFY_GITHUB_PLUGIN_CLIENTID: ${{ secrets.REG_NOTIFY_GITHUB_PLUGIN_CLIENTID }}
+  #     AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+  #     AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+  #     SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

+ 5 - 4
.github/workflows/ci-app.yml

@@ -150,16 +150,17 @@ jobs:
           cache: 'yarn'
           cache-dependency-path: '**/yarn.lock'
 
-      - name: Cache/Restore node_modules
+      - name: Cache/Restore node_modules and next cache files
         id: cache-dependencies
         uses: actions/cache@v3
         with:
           path: |
             **/node_modules
-          key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('packages/app/package.json') }}
+            ${{ github.workspace }}/packages/app/.next/cache
+          key: dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('packages/app/package.json') }}
           restore-keys: |
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+            dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
+            dev-${{ runner.OS }}-node${{ matrix.node-version }}-
 
       - name: lerna bootstrap
         run: |

+ 6 - 1
.github/workflows/reusable-app-prod.yml

@@ -49,6 +49,7 @@ jobs:
     - name: Remove unnecessary packages
       run: |
         rm -rf packages/slackbot-proxy
+        rm -f "node_modules/@growi/slackbot-proxy"
 
     - name: Build
       run: |
@@ -61,6 +62,7 @@ jobs:
       run: |
         tar -cf production.tar \
           package.json \
+          packages/app/.next \
           packages/app/config \
           packages/app/public \
           packages/app/resource \
@@ -81,7 +83,9 @@ jobs:
       uses: actions/upload-artifact@v3
       with:
         name: Bundle Analyzing Report
-        path: packages/app/report/bundle-analyzer.html
+        path: |
+          packages/app/.next/analyze/client.html
+          packages/app/.next/analyze/server.html
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
@@ -143,6 +147,7 @@ jobs:
     - name: Remove unnecessary packages
       run: |
         rm -rf packages/slackbot-proxy
+        rm -f "node_modules/@growi/slackbot-proxy"
 
     - name: lerna bootstrap --production
       run: |

+ 4 - 0
packages/app/.eslintignore

@@ -1,7 +1,11 @@
 /dist/**
+/transpiled/**
 /public/**
+/config/next-i18next.config.js
 /src/client/legacy/thirdparty-js/**
 /src/client/util/reveal/plugins/markdown.js
 /src/linter-checker/**
+/src/utils/next.config.utils.js
+/src-obsolete/**
 /tmp/**
 /next-env.d.ts

+ 11 - 1
packages/app/.eslintrc.js

@@ -38,9 +38,19 @@ module.exports = {
     '@typescript-eslint/no-var-requires': 'off',
 
     // set 'warn' temporarily -- 2021.08.02 Yuki Takei
-    '@typescript-eslint/explicit-module-boundary-types': ['warn'],
     '@typescript-eslint/no-use-before-define': ['warn'],
     '@typescript-eslint/no-this-alias': ['warn'],
     'jest/no-done-callback': ['warn'],
   },
+  overrides: [
+    {
+      // enable the rule specifically for TypeScript files
+      files: ['*.ts', '*.tsx'],
+      rules: {
+        // '@typescript-eslint/explicit-module-boundary-types': ['error'],
+        // set 'warn' temporarily -- 2022.07.25 Yuki Takei
+        '@typescript-eslint/explicit-module-boundary-types': ['warn'],
+      },
+    },
+  ],
 };

+ 4 - 1
packages/app/.gitignore

@@ -11,12 +11,15 @@ test/cypress/videos
 /build/
 /dist/
 /transpiled/
-/report/
 /public/static/js
 /public/static/styles
 /public/uploads
 /tmp/
 
+# transpiled configuration files for production build
+/config/next-i18next.config.js
+/src/utils/next.config.utils.js
+
 # dist (for GROWI v4.x and below)
 /public/*.chunk.js
 /public/*.chunk.js.LICENSE.txt

+ 0 - 0
packages/app/src/next-i18next.config.ts → packages/app/config/next-i18next.config.ts


+ 1 - 0
packages/app/docker/Dockerfile

@@ -111,6 +111,7 @@ RUN yarn lerna run build
 # make artifacts
 RUN tar -cf packages.tar \
   package.json \
+  packages/app/.next \
   packages/app/config \
   packages/app/public \
   packages/app/resource \

+ 91 - 70
packages/app/next.config.js

@@ -1,23 +1,33 @@
-import eazyLogger from 'eazy-logger';
-import { I18NextHMRPlugin } from 'i18next-hmr/plugin';
-import { withSuperjson } from 'next-superjson';
-import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
+/**
+ * == Notes for production build==
+ * The modules required from this file must be transpiled before running `next build`.
+ *
+ * See: https://github.com/vercel/next.js/discussions/35969#discussioncomment-2522954
+ */
 
-import { i18n, localePath } from './src/next-i18next.config';
-import { listScopedPackages, listPrefixedPackages } from './src/utils/next.config.utils';
+const { withSuperjson } = require('next-superjson');
+const { PHASE_PRODUCTION_BUILD, PHASE_PRODUCTION_SERVER } = require('next/constants');
 
 
-// setup logger
-const logger = eazyLogger.Logger({
-  prefix: '[{green:next.config.js}] ',
-  useLevelPrefixes: false,
-});
+// define additional entries
+const additionalWebpackEntries = {
+  boot: './src/client/boot',
+};
 
 
-const setupWithTM = () => {
+const setupTranspileModules = () => {
+  const eazyLogger = require('eazy-logger');
+  const { listScopedPackages, listPrefixedPackages } = require('./src/utils/next.config.utils');
+
+  // setup logger
+  const logger = eazyLogger.Logger({
+    prefix: '[{green:next.config.js}] ',
+    useLevelPrefixes: false,
+  });
+
   // define transpiled packages for '@growi/*'
   const packages = [
-    ...listScopedPackages(['@growi'], { ignorePackageNames: '@growi/app' }),
+    ...listScopedPackages(['@growi'], { ignorePackageNames: ['@growi/app'] }),
     // listing ESM packages until experimental.esmExternals works correctly to avoid ERR_REQUIRE_ESM
     'react-markdown',
     'unified',
@@ -39,63 +49,74 @@ const setupWithTM = () => {
 
   return require('next-transpile-modules')(packages);
 };
-const withTM = setupWithTM();
-
-
-// define additional entries
-const additionalWebpackEntries = {
-  boot: './src/client/boot',
-};
-
 
-/** @type {import('next').NextConfig} */
-const nextConfig = {
-  // == DOES NOT WORK
-  // see: https://github.com/vercel/next.js/discussions/27876
-  // experimental: { esmExternals: true }, // Prefer loading of ES Modules over CommonJS
-
-  reactStrictMode: true,
-  typescript: {
-    tsconfigPath: 'tsconfig.build.client.json',
-  },
-  pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],
-
-  i18n,
-
-  /** @param config {import('next').NextConfig} */
-  webpack(config, options) {
-    // Avoid "Module not found: Can't resolve 'fs'"
-    // See: https://stackoverflow.com/a/68511591
-    if (!options.isServer) {
-      config.resolve.fallback.fs = false;
-    }
-
-    // See: https://webpack.js.org/configuration/externals/
-    // This provides a way of excluding dependencies from the output bundles
-    config.externals.push('dtrace-provider');
-
-    // configure additional entries
-    const orgEntry = config.entry;
-    config.entry = () => {
-      return orgEntry().then((entry) => {
-        return { ...entry, ...additionalWebpackEntries };
-      });
-    };
-
-    config.plugins.push(
-      new WebpackManifestPlugin({
-        fileName: 'custom-manifest.json',
-      }),
-    );
-
-    // setup i18next-hmr
-    if (!options.isServer && options.dev) {
-      config.plugins.push(new I18NextHMRPlugin({ localesDir: localePath }));
-    }
-
-    return config;
-  },
 
+module.exports = async(phase, { defaultConfig }) => {
+
+  const { i18n, localePath } = require('./config/next-i18next.config');
+
+  /** @type {import('next').NextConfig} */
+  const nextConfig = {
+    // == DOES NOT WORK
+    // see: https://github.com/vercel/next.js/discussions/27876
+    // experimental: { esmExternals: true }, // Prefer loading of ES Modules over CommonJS
+
+    reactStrictMode: true,
+    swcMinify: true,
+    typescript: {
+      tsconfigPath: 'tsconfig.build.client.json',
+    },
+    pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],
+
+    i18n,
+
+    /** @param config {import('next').NextConfig} */
+    webpack(config, options) {
+      // Avoid "Module not found: Can't resolve 'fs'"
+      // See: https://stackoverflow.com/a/68511591
+      if (!options.isServer) {
+        config.resolve.fallback.fs = false;
+      }
+
+      // See: https://webpack.js.org/configuration/externals/
+      // This provides a way of excluding dependencies from the output bundles
+      config.externals.push('dtrace-provider');
+
+      // configure additional entries
+      const orgEntry = config.entry;
+      config.entry = () => {
+        return orgEntry().then((entry) => {
+          return { ...entry, ...additionalWebpackEntries };
+        });
+      };
+
+      const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
+      config.plugins.push(
+        new WebpackManifestPlugin({
+          fileName: 'custom-manifest.json',
+        }),
+      );
+
+      // setup i18next-hmr
+      if (!options.isServer && options.dev) {
+        const { I18NextHMRPlugin } = require('i18next-hmr/plugin');
+        config.plugins.push(new I18NextHMRPlugin({ localesDir: localePath }));
+      }
+
+      return config;
+    },
+
+  };
+
+  // production server
+  if (phase === PHASE_PRODUCTION_SERVER) {
+    return withSuperjson()(nextConfig);
+  }
+
+  const withTM = setupTranspileModules();
+  const withBundleAnalyzer = require('@next/bundle-analyzer')({
+    enabled: phase === PHASE_PRODUCTION_BUILD || process.env.ANALYZE === 'true',
+  });
+
+  return withBundleAnalyzer(withTM(withSuperjson()(nextConfig)));
 };
-
-module.exports = withSuperjson()(withTM(nextConfig));

+ 9 - 8
packages/app/package.json

@@ -4,16 +4,14 @@
   "license": "MIT",
   "scripts": {
     "//// for production": "",
-    "build": "yarn next build",
+    "build": "run-p build:*",
     "start": "yarn next start",
-    "//// for production (obsolete)": "",
-    "start:obsolete": "yarn build && yarn server",
-    "build:obsolete": "run-p build:*",
-    "build:client": "yarn cross-env NODE_ENV=production webpack --config config/webpack.prod.js",
+    "build:client": "yarn next build",
+    "prebuild:client": "tsc -p tsconfig.build.next.config.json",
     "build:server": "yarn cross-env NODE_ENV=production tsc -p tsconfig.build.server.json && tsc-alias -p tsconfig.build.server.json",
+    "postbuild:server": "npx -y shx mv transpiled/src dist && npx -y shx cp -r transpiled/config/* config && npx -y shx cp -r src/server/views dist/server/ && npx -y shx rm -rf transpiled",
     "clean": "npx -y shx rm -rf dist transpiled",
     "prebuild": "yarn cross-env NODE_ENV=production run-p clean resources:*",
-    "postbuild": "npx -y shx mv transpiled/src dist && npx -y shx cp -r transpiled/config/* config && npx -y shx cp -r src/server/views dist/server/ && npx -y shx rm -rf transpiled",
     "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
     "server:ci": "yarn server --ci",
     "preserver": "yarn cross-env NODE_ENV=production yarn migrate",
@@ -21,6 +19,7 @@
     "//// for development": "",
     "dev": "yarn cross-env NODE_ENV=development ts-node-dev -r tsconfig-paths/register -r dotenv-flow/config --inspect --transpile-only src/server/app.ts",
     "predev": "yarn cross-env NODE_ENV=development run-p resources:* dev:migrate:up",
+    "dev:analyze": "yarn cross-env ANALYZE=true yarn dev",
     "dev:migrate-mongo": "yarn cross-env NODE_ENV=development yarn ts-node node_modules/.bin/migrate-mongo",
     "dev:migrate": "yarn dev:migrate:up",
     "dev:migrate:create": "yarn dev:migrate-mongo create",
@@ -64,6 +63,7 @@
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
     "@growi/codemirror-textlint": "^5.1.1-RC.0",
+    "@growi/core": "^5.1.1-RC.0",
     "@growi/plugin-attachment-refs": "^5.1.1-RC.0",
     "@growi/plugin-lsx": "^5.1.1-RC.0",
     "@growi/plugin-pukiwiki-like-linker": "^5.1.1-RC.0",
@@ -126,6 +126,7 @@
     "multer-autoreap": "^1.0.3",
     "next": "^12.1.6",
     "next-i18next": "^11.0.0",
+    "next-superjson": "^0.0.4",
     "next-themes": "^0.2.0",
     "nocache": "^3.0.1",
     "nodemailer": "^6.6.2",
@@ -183,6 +184,8 @@
     "@alienfast/i18next-loader": "^1.1.4",
     "@growi/ui": "^5.1.1-RC.0",
     "@handsontable/react": "=2.1.0",
+    "@icon/themify-icons": "1.0.1-alpha.3",
+    "@next/bundle-analyzer": "^12.2.3",
     "@types/compression": "^1.7.0",
     "@types/express": "^4.17.11",
     "@types/jquery": "^3.5.8",
@@ -214,7 +217,6 @@
     "markdown-table": "^1.1.1",
     "material-icons": "^1.11.3",
     "morgan": "^1.10.0",
-    "next-superjson": "^0.0.4",
     "next-transpile-modules": "^9.0.0",
     "normalize-path": "^3.0.0",
     "penpal": "^4.0.0",
@@ -239,7 +241,6 @@
     "sticky-events": "^3.4.11",
     "swagger2openapi": "^5.3.1",
     "swr": "^1.3.0",
-    "@icon/themify-icons": "1.0.1-alpha.3",
     "throttle-debounce": "^3.0.1",
     "toastr": "^2.1.2",
     "ts-node-dev": "^2.0.0",

+ 0 - 0
packages/app/src/client/admin.jsx → packages/app/src-obsolete/client/admin.jsx


+ 0 - 0
packages/app/src/client/app.jsx → packages/app/src-obsolete/client/app.jsx


+ 0 - 0
packages/app/src/client/base.jsx → packages/app/src-obsolete/client/base.jsx


+ 0 - 0
packages/app/src/client/installer.jsx → packages/app/src-obsolete/client/installer.jsx


+ 0 - 0
packages/app/src/client/nologin.jsx → packages/app/src-obsolete/client/nologin.jsx


+ 1 - 1
packages/app/src/client/util/apiv3-client.ts

@@ -13,7 +13,7 @@ const apiv3Root = '/_api/v3';
 const logger = loggerFactory('growi:apiv3');
 
 
-const apiv3ErrorHandler = (_err) => {
+const apiv3ErrorHandler = (_err: any): any[] => {
   // extract api errors from general 400 err
   const err = _err.response ? _err.response.data.errors : _err;
   const errs = toArrayIfNot(err);

+ 1 - 1
packages/app/src/client/util/i18n.js

@@ -17,7 +17,7 @@ Object.values(locales).forEach((locale) => {
 });
 
 /*
-* Note: This file will be deleted. use "~/next-i18next.config" instead
+* Note: This file will be deleted. use "^/config/next-i18next.config" instead
 */
 // extract metadata list from 'public/static/locales/${locale}/meta.json'
 export const localeMetadatas = Object.values(locales).map(locale => locale.meta);

+ 2 - 1
packages/app/src/components/Admin/App/AppSetting.jsx

@@ -3,9 +3,10 @@ import React, { useCallback } from 'react';
 import { useTranslation, i18n } from 'next-i18next';
 import PropTypes from 'prop-types';
 
+import { i18n as i18nConfig } from '^/config/next-i18next.config';
+
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
-import { i18n as i18nConfig } from '~/next-i18next.config';
 import loggerFactory from '~/utils/logger';
 
 

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx

@@ -12,7 +12,7 @@ const CustomizeLayoutSetting = (): JSX.Element => {
   const { resolvedTheme } = useNextThemes();
 
   const [isContainerFluid, setIsContainerFluid] = useState(false);
-  const [retrieveError, setRetrieveError] = useState();
+  const [retrieveError, setRetrieveError] = useState<any>();
 
   const retrieveData = useCallback(async() => {
     try {

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeLogoSetting.tsx

@@ -19,7 +19,7 @@ const CustomizeLogoSetting = (): JSX.Element => {
   const [uploadLogoSrc, setUploadLogoSrc] = useState<ArrayBuffer | string | null>(null);
   const [isImageCropModalShow, setIsImageCropModalShow] = useState<boolean>(false);
   const [isDefaultLogo, setIsDefaultLogo] = useState<boolean>(true);
-  const [retrieveError, setRetrieveError] = useState<string | null>(null);
+  const [retrieveError, setRetrieveError] = useState<any>();
   const [customizedLogoSrc, setCustomizedLogoSrc] = useState< string | null >(null);
 
   const retrieveData = useCallback(async() => {

+ 6 - 4
packages/app/src/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx

@@ -44,13 +44,15 @@ const ElasticsearchManagement = () => {
       setAliasesData(info.aliases);
       setIsNormalized(info.isNormalized);
     }
-    catch (errors) {
+    catch (errors: unknown) {
       setIsConnected(false);
 
       // evaluate whether configured or not
-      for (const error of errors) {
-        if (error.code === 'search-service-unconfigured') {
-          setIsConfigured(false);
+      if (Array.isArray(errors)) {
+        for (const error of errors) {
+          if (error.code === 'search-service-unconfigured') {
+            setIsConfigured(false);
+          }
         }
       }
 

+ 2 - 1
packages/app/src/pages/_app.page.tsx

@@ -10,7 +10,8 @@ import '~/styles/style-next.scss';
 // import '~/styles/theme/default.scss';
 // import InterceptorManager from '~/service/interceptor-manager';
 
-import * as nextI18nConfig from '../next-i18next.config';
+import * as nextI18nConfig from '^/config/next-i18next.config';
+
 import { useI18nextHMR } from '../services/i18next-hmr';
 import {
   useAppTitle, useConfidential, useGrowiTheme, useGrowiVersion, useSiteUrl,

+ 2 - 1
packages/app/src/pages/utils/commons.ts

@@ -2,10 +2,11 @@ import { DevidedPagePath, Lang } from '@growi/core';
 import { GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { SSRConfig, UserConfig } from 'next-i18next';
 
+import * as nextI18NextConfig from '^/config/next-i18next.config';
+
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { GrowiThemes } from '~/interfaces/theme';
 
-import * as nextI18NextConfig from '../../next-i18next.config';
 
 export type CommonProps = {
   namespacesRequired: string[], // i18next

+ 2 - 1
packages/app/src/server/crowi/dev.js

@@ -1,6 +1,7 @@
 import path from 'path';
 
-import { i18n } from '~/next-i18next.config';
+import { i18n } from '^/config/next-i18next.config';
+
 import loggerFactory from '~/utils/logger';
 
 import nextFactory from '../routes/next';

+ 2 - 1
packages/app/src/server/crowi/express-init.js

@@ -1,7 +1,8 @@
 import csrf from 'csurf';
 import mongoose from 'mongoose';
 
-import { i18n, localePath } from '~/next-i18next.config';
+import { i18n, localePath } from '^/config/next-i18next.config';
+
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:crowi:express-init');

+ 2 - 1
packages/app/src/server/models/user.js

@@ -1,5 +1,6 @@
 /* eslint-disable no-use-before-define */
-import { i18n } from '~/next-i18next.config';
+import { i18n } from '^/config/next-i18next.config';
+
 import { generateGravatarSrc } from '~/utils/gravatar';
 import loggerFactory from '~/utils/logger';
 

+ 2 - 1
packages/app/src/server/routes/apiv3/app-settings.js

@@ -1,7 +1,8 @@
 import { body } from 'express-validator';
 
+import { i18n } from '^/config/next-i18next.config';
+
 import { SupportedAction } from '~/interfaces/activity';
-import { i18n } from '~/next-i18next.config';
 import loggerFactory from '~/utils/logger';
 
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';

+ 2 - 1
packages/app/src/server/routes/apiv3/personal-setting.js

@@ -1,7 +1,8 @@
 import { body } from 'express-validator';
 
+import { i18n } from '^/config/next-i18next.config';
+
 import { SupportedAction } from '~/interfaces/activity';
-import { i18n } from '~/next-i18next.config';
 import loggerFactory from '~/utils/logger';
 
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';

+ 1 - 1
packages/app/src/utils/array-utils.ts

@@ -1,6 +1,6 @@
 // converts non-array item to array
 
-export const toArrayIfNot = (item?: unknown): any[] => {
+export const toArrayIfNot = <T = unknown>(item?: T): T[] => {
   if (item == null) {
     return [];
   }

+ 14 - 11
packages/app/src/utils/next.config.utils.js → packages/app/src/utils/next.config.utils.ts

@@ -1,16 +1,19 @@
 // workaround by https://github.com/martpie/next-transpile-modules/issues/143#issuecomment-817467144
 
-const fs = require('fs');
-const path = require('path');
+import fs from 'fs';
+import path from 'path';
 
 const nodeModulesPath = path.resolve(__dirname, '../../../../node_modules');
 
+type Opts = {
+  ignorePackageNames: string[],
+}
 
-const defaultOpts = { ignorePackageNames: [] };
+const defaultOpts: Opts = { ignorePackageNames: [] };
 
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export const listScopedPackages = (scopes, opts = defaultOpts) => {
-  const scopedPackages = [];
+export const listScopedPackages = (scopes: string[], opts = defaultOpts) => {
+  const scopedPackages: string[] = [];
 
   fs.readdirSync(nodeModulesPath)
     .filter(name => scopes.includes(name))
@@ -18,13 +21,13 @@ export const listScopedPackages = (scopes, opts = defaultOpts) => {
       fs.readdirSync(path.resolve(nodeModulesPath, scope))
         .filter(name => !name.startsWith('.'))
         .forEach((folderName) => {
-          const { name, ignoreTranspileModules } = require(path.resolve(
+          const { name } = require(path.resolve(
             nodeModulesPath,
             scope,
             folderName,
             'package.json',
           ));
-          if (!ignoreTranspileModules && !opts.ignorePackageNames.includes(name)) {
+          if (!opts.ignorePackageNames.includes(name)) {
             scopedPackages.push(name);
           }
         });
@@ -34,19 +37,19 @@ export const listScopedPackages = (scopes, opts = defaultOpts) => {
 };
 
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export const listPrefixedPackages = (prefixes, opts = defaultOpts) => {
-  const prefixedPackages = [];
+export const listPrefixedPackages = (prefixes: string[], opts = defaultOpts) => {
+  const prefixedPackages: string[] = [];
 
   fs.readdirSync(nodeModulesPath)
     .filter(name => prefixes.some(prefix => name.startsWith(prefix)))
     .filter(name => !name.startsWith('.'))
     .forEach((folderName) => {
-      const { name, ignoreTranspileModules } = require(path.resolve(
+      const { name } = require(path.resolve(
         nodeModulesPath,
         folderName,
         'package.json',
       ));
-      if (!ignoreTranspileModules && !opts.ignorePackageNames.includes(name)) {
+      if (!opts.ignorePackageNames.includes(name)) {
         prefixedPackages.push(name);
       }
     });

+ 1 - 1
packages/app/tsconfig.base.json

@@ -5,6 +5,6 @@
     "jsx": "preserve",
     "incremental": true
   },
-  "include": ["next-env.d.ts", "src/**/*"],
+  "include": ["next-env.d.ts", "config/**/*", "src/**/*"],
   "exclude": ["node_modules"]
 }

+ 1 - 3
packages/app/tsconfig.build.client.json

@@ -2,10 +2,8 @@
   "extends": "./tsconfig.base.json",
   "compilerOptions": {
     "module": "esnext",
-    "strict": true,
+    "strict": false,
     "noFallthroughCasesInSwitch": true,
-    "noUnusedLocals": true,
-    "noUnusedParameters": true,
     "baseUrl": ".",
     "paths": {
       "~/*": ["./src/*"],

+ 17 - 0
packages/app/tsconfig.build.next.config.json

@@ -0,0 +1,17 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "noResolve": false,
+    "preserveConstEnums": true,
+    "sourceMap": false,
+    "noEmit": false,
+    "paths": {
+      "~/*": ["./src/*"],
+      "^/*": ["./*"]
+    }
+  },
+  "files": [
+    "config/next-i18next.config.ts",
+    "src/utils/next.config.utils.ts"
+  ]
+}

+ 1 - 0
packages/codemirror-textlint/package.json

@@ -5,6 +5,7 @@
   "main": "dist/index.js",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
+    "clean": "npx -y shx rm -rf dist",
     "tsc": "tsc -p tsconfig.build.json",
     "tsc:w": "yarn tsc -w",
     "lint": "eslint src --ext .ts",

+ 1 - 0
packages/core/package.json

@@ -15,6 +15,7 @@
     "build": "run-p build:*",
     "build:cjs": "tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json",
     "build:esm": "tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json",
+    "clean": "npx -y shx rm -rf dist",
     "lint:js": "eslint **/*.{js,ts}",
     "lint": "npm-run-all -p lint:*",
     "test": "jest --verbose"

+ 3 - 2
packages/plugin-attachment-refs/package.json

@@ -16,6 +16,7 @@
     "build": "run-p build:*",
     "build:cjs": "tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json",
     "build:esm": "tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json",
+    "clean": "npx -y shx rm -rf dist",
     "lint:js": "eslint **/*.{js,jsx,ts,tsx}",
     "lint:styles": "stylelint src/**/*.scss src/**/*.css",
     "lint": "run-p lint:*",
@@ -32,7 +33,7 @@
   "devDependencies": {
     "eslint-plugin-regex": "^1.8.0",
     "npm-run-all": "^4.1.5",
-    "react": "^16.8.3",
-    "react-dom": "^16.8.3"
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
   }
 }

+ 3 - 2
packages/plugin-lsx/package.json

@@ -16,6 +16,7 @@
     "build": "run-p build:*",
     "build:cjs": "tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json",
     "build:esm": "tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json",
+    "clean": "npx -y shx rm -rf dist",
     "lint:js": "eslint **/*.{js,jsx,ts,tsx}",
     "lint:styles": "stylelint src/**/*.scss src/**/*.css",
     "lint": "run-p lint:*",
@@ -24,7 +25,7 @@
   "dependencies": {},
   "devDependencies": {
     "eslint-plugin-regex": "^1.8.0",
-    "react": "^16.8.3",
-    "react-dom": "^16.8.3"
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
   }
 }

+ 1 - 0
packages/plugin-pukiwiki-like-linker/package.json

@@ -16,6 +16,7 @@
     "build": "run-p build:*",
     "build:cjs": "tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json",
     "build:esm": "tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json",
+    "clean": "npx -y shx rm -rf dist",
     "lint:js": "eslint **/*.{js,ts}",
     "lint": "run-p lint:*",
     "test": ""

+ 1 - 0
packages/slack/package.json

@@ -6,6 +6,7 @@
   "typings": "dist/index.d.ts",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
+    "clean": "npx -y shx rm -rf dist",
     "tsc": "tsc -p tsconfig.build.json",
     "tsc:w": "yarn tsc -w",
     "test": "cross-env NODE_ENV=test jest --passWithNoTests",

+ 1 - 0
packages/slackbot-proxy/package.json

@@ -4,6 +4,7 @@
   "license": "MIT",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
+    "clean": "npx -y shx rm -rf dist",
     "cp:public": "cp -RT ./src/public ./dist/public",
     "cp:views": "cp -RT ./src/views ./dist/views",
     "cp:bootstrap": "cp -RT ./node_modules/bootstrap/dist ./dist/public/bootstrap",

+ 1 - 0
packages/ui/.gitignore

@@ -0,0 +1 @@
+/dist

+ 9 - 1
packages/ui/package.json

@@ -6,17 +6,25 @@
   "keywords": [
     "growi"
   ],
+  "main": "dist/cjs/index.js",
   "module": "dist/esm/index.js",
   "files": [
     "dist"
   ],
   "scripts": {
+    "build": "run-p build:*",
+    "build:cjs": "tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json",
+    "build:esm": "tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json",
+    "clean": "npx -y shx rm -rf dist",
     "lint:js": "eslint **/*.{js,jsx,ts,tsx}",
     "lint": "npm-run-all -p lint:*",
     "test": "jest --verbose"
   },
+  "dependencies": {
+    "@growi/core": "^5.1.1-RC.0"
+  },
   "devDependencies": {
     "eslint-plugin-regex": "^1.8.0",
-    "react": "^16.8.3"
+    "react": "^18.2.0"
   }
 }

+ 18 - 0
packages/ui/tsconfig.build.cjs.json

@@ -0,0 +1,18 @@
+{
+  "extends": "./tsconfig.base.json",
+  "compilerOptions": {
+    "module": "commonjs",
+
+    "rootDir": "./src",
+    "outDir": "dist/cjs",
+    "declaration": true,
+    "noResolve": false,
+    "preserveConstEnums": true,
+    "sourceMap": false,
+    "noEmit": false,
+
+    "baseUrl": ".",
+    "paths": {
+    }
+  }
+}

+ 16 - 0
packages/ui/tsconfig.build.esm.json

@@ -0,0 +1,16 @@
+{
+  "extends": "./tsconfig.base.json",
+  "compilerOptions": {
+    "rootDir": "./src",
+    "outDir": "dist/esm",
+    "declaration": true,
+    "noResolve": false,
+    "preserveConstEnums": true,
+    "sourceMap": false,
+    "noEmit": false,
+
+    "baseUrl": ".",
+    "paths": {
+    }
+  }
+}

+ 77 - 4
yarn.lock

@@ -3125,6 +3125,13 @@
     semver "^7.3.5"
     tar "^6.1.11"
 
+"@next/bundle-analyzer@^12.2.3":
+  version "12.2.3"
+  resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.2.3.tgz#8b4b934d28c09b9c11c4a074fbcc444402c8e017"
+  integrity sha512-tRgyo5afC02eofvnN9IerUvTJpxV+eQH4S02hc1U4oJJOhpHbwRinFUE3u4SNWbT5wSFofv1tnw5rYhyX7W8wQ==
+  dependencies:
+    webpack-bundle-analyzer "4.3.0"
+
 "@next/env@12.1.6":
   version "12.1.6"
   resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.6.tgz#5f44823a78335355f00f1687cfc4f1dafa3eca08"
@@ -3499,6 +3506,11 @@
     tiny-glob "^0.2.9"
     tslib "^2.4.0"
 
+"@polka/url@^1.0.0-next.20":
+  version "1.0.0-next.21"
+  resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
+  integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
+
 "@popperjs/core@^2.8.6":
   version "2.11.4"
   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.4.tgz#d8c7b8db9226d2d7664553a0741ad7d0397ee503"
@@ -4817,7 +4829,7 @@ acorn-walk@^7.1.1:
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
   integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
 
-acorn-walk@^8.1.1:
+acorn-walk@^8.0.0, acorn-walk@^8.1.1:
   version "8.2.0"
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
   integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
@@ -4827,6 +4839,11 @@ acorn@^7.1.1:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
   integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
 
+acorn@^8.0.4:
+  version "8.8.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
+  integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+
 acorn@^8.2.4:
   version "8.4.1"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c"
@@ -6874,7 +6891,7 @@ commander@^5.1.0:
   resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
   integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
 
-commander@^6.2.1:
+commander@^6.2.0, commander@^6.2.1:
   version "6.2.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
   integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
@@ -8181,6 +8198,11 @@ duplexer@^0.1.1, duplexer@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
 
+duplexer@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
+  integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
+
 duplexify@^4.0.0, duplexify@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61"
@@ -10587,6 +10609,13 @@ gud@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
 
+gzip-size@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
+  integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
+  dependencies:
+    duplexer "^0.1.2"
+
 handlebars@^4.7.6:
   version "4.7.7"
   resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
@@ -14927,6 +14956,11 @@ mri@^1.1.0:
   resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
   integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
 
+mrmime@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
+  integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==
+
 ms@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -15864,6 +15898,11 @@ open@^8.0.0, open@^8.4.0:
     is-docker "^2.1.1"
     is-wsl "^2.2.0"
 
+opener@^1.5.2:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
+  integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
+
 openid-client@^5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.1.2.tgz#a80cc6a7d8d7159ad97c51781338f5a954396bba"
@@ -17202,7 +17241,7 @@ react-dnd@^14.0.5:
     fast-deep-equal "^3.1.3"
     hoist-non-react-statics "^3.3.2"
 
-react-dom@^16.2.0, react-dom@^16.8.3:
+react-dom@^16.2.0:
   version "16.14.0"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
   integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
@@ -17442,7 +17481,7 @@ react-waypoint@^10.1.0:
     prop-types "^15.0.0"
     react-is "^17.0.1"
 
-react@^16.2.0, react@^16.8.3:
+react@^16.2.0:
   version "16.14.0"
   resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
   integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
@@ -18971,6 +19010,15 @@ simplebar@^5.3.6:
     lodash.memoize "^4.1.2"
     lodash.throttle "^4.1.1"
 
+sirv@^1.0.7:
+  version "1.0.19"
+  resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
+  integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==
+  dependencies:
+    "@polka/url" "^1.0.0-next.20"
+    mrmime "^1.0.0"
+    totalist "^1.0.0"
+
 sisteransi@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c"
@@ -20644,6 +20692,11 @@ toidentifier@1.0.1:
   resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
   integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
 
+totalist@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
+  integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
+
 tough-cookie@^2.3.3, tough-cookie@~2.5.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@@ -21743,6 +21796,21 @@ webidl-conversions@^7.0.0:
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
   integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
 
+webpack-bundle-analyzer@4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b"
+  integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==
+  dependencies:
+    acorn "^8.0.4"
+    acorn-walk "^8.0.0"
+    chalk "^4.1.0"
+    commander "^6.2.0"
+    gzip-size "^6.0.0"
+    lodash "^4.17.20"
+    opener "^1.5.2"
+    sirv "^1.0.7"
+    ws "^7.3.1"
+
 webpack-manifest-plugin@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz#084246c1f295d1b3222d36e955546433ca8df803"
@@ -21976,6 +22044,11 @@ write@1.0.3:
   dependencies:
     mkdirp "^0.5.1"
 
+ws@^7.3.1:
+  version "7.5.9"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
+  integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
+
 ws@^7.4.6:
   version "7.5.1"
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"