Projects STRLCPY gradejs Commits e4b0ad93
🤬
  • feat: add react helmet for seo tags (#59)

    * feat: add react helmet for seo tags
    
    * fix: slight optimizations
    
    * fix: review
    
    * fix: review
  • Loading...
  • Oleg Klimenko committed with GitHub 2 years ago
    e4b0ad93
    1 parent 7c7a38fb
  • ■ ■ ■ ■
    cli/local_start.sh
    skipped 53 lines
    54 54  API_PID=$!
    55 55   
    56 56  echo "Starting web package dev server"
    57  -PORT=3000 API_ORIGIN=http://localhost:8083 CORS_ORIGIN=http://localhost:3000 PLAUSIBLE_DOMAIN= GA_ID= DUMP_ANALYTICS= \
     57 +PORT=3000 PUBLIC_ROOT_URL=http://localhost:3000 API_ORIGIN=http://localhost:8083 CORS_ORIGIN=http://localhost:3000 PLAUSIBLE_DOMAIN= GA_ID= DUMP_ANALYTICS= \
    58 58   npm run dev:start --prefix packages/web 2>&1 &
    59 59  WEB_PID=$!
    60 60   
    skipped 55 lines
  • ■ ■ ■ ■
    package.json
    skipped 21 lines
    22 22   "build:public-api": "yarn workspace @gradejs-public/public-api run build",
    23 23   "build:backend": "yarn build:shared && yarn build:public-api && yarn build:worker",
    24 24   "dev:start": "bash cli/local_start.sh",
    25  - "dev:start:web": "PORT=3000 API_ORIGIN=https://api.staging.gradejs.com npm run dev:start --prefix packages/web"
     25 + "dev:start:web": "PORT=3000 PUBLIC_ROOT_URL=http://localhost:3000 API_ORIGIN=https://api.staging.gradejs.com npm run dev:start --prefix packages/web"
    26 26   },
    27 27   "devDependencies": {
    28 28   "@swc/core": "^1.2.233",
    skipped 15 lines
  • ■ ■ ■ ■ ■ ■
    packages/shared/src/utils/env.ts
    skipped 6 lines
    7 7   // Web related
    8 8   PublicApiOrigin = 'API_ORIGIN',
    9 9   PublicCorsOrigin = 'CORS_ORIGIN',
     10 + PublicRootUrl = 'PUBLIC_ROOT_URL',
    10 11   PlausibleDomain = 'PLAUSIBLE_DOMAIN',
    11 12   AnalyticsId = 'GA_ID',
    12 13   VerboseAnalytics = 'DUMP_ANALYTICS',
    skipped 40 lines
    53 54   return origins.split(',').map((origin) => origin.trim());
    54 55  };
    55 56   
     57 +export const getPublicRootUrl = () => getEnv(Env.PublicRootUrl);
     58 + 
    56 59  export function getEnv(name: string, defaultValue?: string) {
    57 60   const value = process.env[name];
    58 61   
    skipped 14 lines
    73 76   
    74 77  export const getClientVars = () => {
    75 78   return [
     79 + Env.PublicRootUrl,
    76 80   Env.PublicApiOrigin,
    77 81   Env.PublicCorsOrigin,
    78 82   Env.PlausibleDomain,
    skipped 8 lines
  • ■ ■ ■ ■ ■
    packages/web/package.json
    skipped 34 lines
    35 35   "@types/react-dom": "^17.0.9",
    36 36   "@types/react-gtm-module": "^2.0.1",
    37 37   "@types/semver": "^7.3.9",
     38 + "@types/webpack-node-externals": "^2.5.3",
    38 39   "babel-loader": "^8.2.2",
    39 40   "copy-webpack-plugin": "^11.0.0",
    40 41   "css-loader": "^6.2.0",
    skipped 12 lines
    53 54   "typescript": "^4.6.4",
    54 55   "webpack": "^5.52.1",
    55 56   "webpack-cli": "^4.8.0",
    56  - "webpack-dev-server": "^4.2.1"
     57 + "webpack-dev-server": "^4.2.1",
     58 + "webpack-node-externals": "^3.0.0",
     59 + "webpack-stats-plugin": "^1.1.0"
    57 60   },
    58 61   "dependencies": {
    59 62   "@gradejs-public/shared": "^0.1.0",
    60 63   "@reduxjs/toolkit": "^1.8.3",
    61 64   "@trpc/client": "^9.27.0",
    62 65   "@types/lodash.memoize": "^4.1.7",
     66 + "@types/react-helmet": "^6.1.5",
    63 67   "clsx": "^1.1.1",
    64 68   "express": "^4.18.1",
    65 69   "lodash.memoize": "^4.1.2",
    skipped 1 lines
    67 71   "react": "^17.0.2",
    68 72   "react-dom": "^17.0.2",
    69 73   "react-ga": "^3.3.0",
     74 + "react-helmet": "^6.1.0",
    70 75   "react-hook-form": "^7.15.3",
    71 76   "react-redux": "^8.0.2",
    72 77   "react-router-dom": "^6.3.0",
    skipped 4 lines
  • ■ ■ ■ ■ ■
    packages/web/src/components/App.tsx
     1 +import { getPublicRootUrl } from '../../../shared/src/utils/env';
    1 2  import React from 'react';
     3 +import { Helmet } from 'react-helmet';
    2 4  import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
    3 5  import { HomePage } from './pages/Home';
    4 6  import { WebsiteResultsPage } from './pages/WebsiteResults';
    skipped 13 lines
    18 20   const location = useLocation();
    19 21   locationChangeHandler(location.pathname);
    20 22   return (
    21  - <Routes>
    22  - <Route index element={<HomePage />} />
    23  - <Route path='/w/:hostname' element={<WebsiteResultsPage />} />
    24  - <Route path='*' element={<Navigate replace to='/' />} />
    25  - </Routes>
     23 + <>
     24 + <Helmet>
     25 + <title>Production Webpack Bundle Analyzer - GradeJS</title>
     26 + <meta
     27 + name='description'
     28 + content='GradeJS analyzes production JavaScript files and matches bundled NPM packages with specific version precision.'
     29 + />
     30 + 
     31 + <meta property='og:url' content={getPublicRootUrl() + location.pathname} />
     32 + <meta property='og:type' content='website' />
     33 + <meta property='og:title' content='Production Webpack Bundle Analyzer - GradeJS' />
     34 + <meta
     35 + property='og:description'
     36 + content='GradeJS analyzes production JavaScript files and matches bundled NPM packages with specific version precision.'
     37 + />
     38 + <meta property='og:image' content='/static/sharing-image.png' />
     39 + </Helmet>
     40 + <Routes>
     41 + <Route index element={<HomePage />} />
     42 + <Route path='/w/:hostname' element={<WebsiteResultsPage />} />
     43 + <Route path='*' element={<Navigate replace to='/' />} />
     44 + </Routes>
     45 + </>
    26 46   );
    27 47  }
    28 48   
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/Layout.tsx
     1 +import * as React from 'react';
     2 +import { HelmetData } from 'react-helmet';
     3 + 
     4 +const escapedCharacterRegexp = /[<>\/\u2028\u2029]/g;
     5 +const escapedCharacterMap = {
     6 + '<': '\\u003C',
     7 + '>': '\\u003E',
     8 + '/': '\\u002F',
     9 + '\u2028': '\\u2028',
     10 + '\u2029': '\\u2029',
     11 +};
     12 + 
     13 +export default function sanitizeJSON(value: any) {
     14 + return JSON.stringify(value).replace(
     15 + escapedCharacterRegexp,
     16 + (character) => escapedCharacterMap[character as keyof typeof escapedCharacterMap]
     17 + );
     18 +}
     19 + 
     20 +export function Layout({
     21 + js,
     22 + css,
     23 + head,
     24 + env,
     25 + html,
     26 +}: {
     27 + js: string[];
     28 + css: string[];
     29 + head: HelmetData;
     30 + env: Record<string, string>;
     31 + html: string; // pre-rendered components
     32 +}) {
     33 + return (
     34 + <html lang='en'>
     35 + <head>
     36 + {head.title.toComponent()}
     37 + {head.meta.toComponent()}
     38 + <meta charSet='utf-8' />
     39 + <meta name='viewport' content='width=device-width, initial-scale=1' />
     40 + <meta name='mobile-web-app-capable' content='yes' />
     41 + {/* TODO: preload all fonts and add icons after redesign
     42 + <link rel="preload" href={} as="font" type="font/woff2"/>
     43 + <link rel='icon' type='image/svg+xml' href={'TODO'} />
     44 + <link rel='icon' type='image/png' href={'TODO'} />
     45 + */}
     46 + {css.map((cssFile) => (
     47 + <link key={cssFile} rel='stylesheet' href={cssFile} />
     48 + ))}
     49 + </head>
     50 + 
     51 + <body {...head.bodyAttributes.toComponent()}>
     52 + <div
     53 + id='app'
     54 + dangerouslySetInnerHTML={{
     55 + __html: html,
     56 + }}
     57 + />
     58 + 
     59 + <script
     60 + type='text/javascript'
     61 + dangerouslySetInnerHTML={{ __html: `window.process = ${sanitizeJSON({ env })}` }}
     62 + />
     63 + {js.map((jsFile) => (
     64 + <script key={jsFile} src={jsFile} />
     65 + ))}
     66 + </body>
     67 + </html>
     68 + );
     69 +}
     70 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/pages/WebsiteResults.tsx
    1 1  import React, { useEffect } from 'react';
     2 +import { Helmet } from 'react-helmet';
    2 3  import { useParams, useNavigate } from 'react-router-dom';
    3 4  import { Error as ErrorLayout, Website } from 'components/layouts';
    4 5  import { trackCustomEvent } from '../../services/analytics';
    skipped 12 lines
    17 18   const dispatch = useAppDispatch();
    18 19   const { webpages, vulnerabilities } = useAppSelector(selectors.default);
    19 20   const packagesFiltered = useAppSelector(selectors.packagesSortedAndFiltered);
     21 + const packagesStats = useAppSelector(selectors.packagesStats);
    20 22   const { isProtected, isPending, isLoading, isFailed, isInvalid } = useAppSelector(
    21 23   selectors.stateFlags
    22 24   );
    skipped 61 lines
    84 86   />
    85 87   );
    86 88   }
     89 + 
     90 + const title = `List of NPM packages that are used on ${hostname} - GradeJS`;
     91 + const description =
     92 + `GradeJS has discovered ${packagesStats.total} NPM packages used on ${hostname}` +
     93 + (packagesStats.vulnerable > 0 ? `, ${packagesStats.vulnerable} are vulnerable` : '') +
     94 + (packagesStats.outdated > 0 ? `, ${packagesStats.outdated} are outdated` : '');
     95 + 
    87 96   return (
    88  - <Website
    89  - isLoading={isLoading}
    90  - isPending={isPending}
    91  - webpages={webpages}
    92  - packages={packagesFiltered}
    93  - host={hostname ?? ''}
    94  - vulnerabilities={vulnerabilities}
    95  - onFiltersApply={setFilters}
    96  - />
     97 + <>
     98 + <Helmet>
     99 + <title>{title}</title>
     100 + <meta name='description' content={description} />
     101 + <meta property='og:title' content={title} />
     102 + <meta property='og:description' content={description} />
     103 + </Helmet>
     104 + <Website
     105 + isLoading={isLoading}
     106 + isPending={isPending}
     107 + webpages={webpages}
     108 + packages={packagesFiltered}
     109 + host={hostname ?? ''}
     110 + vulnerabilities={vulnerabilities}
     111 + onFiltersApply={setFilters}
     112 + />
     113 + </>
    97 114   );
    98 115  }
    99 116   
  • ■ ■ ■ ■ ■
    packages/web/src/server.tsx
    skipped 1 lines
    2 2  import React from 'react';
    3 3  import { Provider } from 'react-redux';
    4 4  import ReactDOMServer from 'react-dom/server';
     5 +import Helmet from 'react-helmet';
    5 6  import { StaticRouter } from 'react-router-dom/server';
    6 7  // TODO: fix import from different monorepo package
    7 8  import { getPort, getClientVars } from '../../shared/src/utils/env';
    8 9  import { store } from './store';
    9 10  import { App } from './components/App';
    10 11  import path from 'path';
    11  -import { readFileSync, readFile } from 'fs';
     12 +import { readFile } from 'fs';
     13 +import { Layout } from 'components/Layout';
    12 14   
    13 15  const app = express();
    14  -const layout = readFileSync(path.resolve(__dirname, 'static', 'index.html'), { encoding: 'utf-8' });
     16 +const staticDir = '/static';
    15 17   
    16  -app.use('/static', express.static(path.join(__dirname, 'static')));
     18 +app.use(staticDir, express.static(path.join(__dirname, 'static')));
    17 19  app.get('/robots.txt', (_, res) =>
    18 20   readFile(path.join(__dirname, '/robots.txt'), { encoding: 'utf-8' }, (err, data) => {
    19 21   if (!err) {
    skipped 5 lines
    25 27   })
    26 28  );
    27 29   
     30 +function getScripts(statsStr: string) {
     31 + let stats: Record<string, string[]>;
     32 + const assets: { js: string[]; css: string[] } = { js: [], css: [] };
     33 + try {
     34 + stats = JSON.parse(statsStr).assetsByChunkName as Record<string, string[]>;
     35 + } catch (e) {
     36 + return assets;
     37 + }
     38 + 
     39 + return Object.values(stats)
     40 + .flat()
     41 + .reduce((acc, asset) => {
     42 + if (asset.endsWith('.js')) {
     43 + acc.js.push(staticDir + '/' + asset);
     44 + }
     45 + if (asset.endsWith('.css')) {
     46 + acc.css.push(staticDir + '/' + asset);
     47 + }
     48 + return acc;
     49 + }, assets);
     50 +}
     51 + 
    28 52  app.get('*', (req, res) => {
    29 53   const html = ReactDOMServer.renderToString(
    30 54   <Provider store={store}>
    skipped 3 lines
    34 58   </Provider>
    35 59   );
    36 60   
    37  - res.send(
    38  - layout
    39  - .replace('<div id="app"></div>', '<div id="app">' + html + '</div>')
    40  - // Little magic to support process.env calls on client side without additional replacements in bundle
    41  - .replace(
    42  - 'window.process = { env: {} };',
    43  - 'window.process = { env: ' + JSON.stringify(getClientVars()) + ' };'
    44  - )
    45  - );
     61 + const helmet = Helmet.renderStatic();
     62 + 
     63 + readFile(path.join(__dirname, 'static', 'stats.json'), { encoding: 'utf-8' }, (err, stats) => {
     64 + if (err) {
     65 + res.status(404).send();
     66 + return;
     67 + }
     68 + 
     69 + const { js, css } = getScripts(stats);
     70 + 
     71 + res.send(
     72 + '<!doctype html>' +
     73 + ReactDOMServer.renderToString(
     74 + <Layout js={js} css={css} head={helmet} env={getClientVars()} html={html} />
     75 + )
     76 + );
     77 + });
    46 78  });
    47 79   
    48 80  app.listen(getPort(8080));
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/src/store/selectors/websiteResults.ts
    skipped 104 lines
    105 105   isPending: webpages.length === 0 || webpages.some((item) => item.status === 'pending'),
    106 106   isProtected: webpages.some((item) => item.status === 'protected'),
    107 107   })),
     108 + packagesStats: createSelector([getPackages, getVulnerabilities], (packages, vulnerabilities) => ({
     109 + total: packages.length,
     110 + vulnerable: packages.filter((pkg) => (vulnerabilities[pkg.packageName]?.length ?? 0) > 0)
     111 + .length,
     112 + outdated: packages.filter(
     113 + (pkg) =>
     114 + pkg.registryMetadata &&
     115 + semver.gtr(pkg.registryMetadata.latestVersion, pkg.packageVersionRange)
     116 + ).length,
     117 + })),
    108 118   packagesSortedAndFiltered: createSelector(
    109 119   [getPackages, getVulnerabilities, getSorting, getFilter, getPackageNameFilter],
    110 120   (packages, vulnerabilities, sorting, filter, packageNameFilter) =>
    skipped 8 lines
  • ■ ■ ■ ■ ■
    packages/web/webpack/client.ts
    skipped 1 lines
    2 2  import HtmlWebpackPlugin from 'html-webpack-plugin';
    3 3  import MiniCssExtractPlugin from 'mini-css-extract-plugin';
    4 4  import CopyPlugin from 'copy-webpack-plugin';
     5 +// eslint-disable-next-line @typescript-eslint/no-var-requires
     6 +const { StatsWriterPlugin } = require('webpack-stats-plugin');
    5 7  import { configCommon, pluginsCommon, srcDir } from './common';
    6 8  import { Configuration } from 'webpack';
    7 9  import { WebpackConfigOptions } from './config';
    skipped 72 lines
    80 82   patterns: [{ from: 'src/assets/sharing-image.png', to: 'sharing-image.png' }],
    81 83   }),
    82 84   ...plugins,
     85 + new StatsWriterPlugin({
     86 + filename: 'stats.json',
     87 + }),
    83 88   ],
    84 89   output: {
    85 90   filename: 'bundle.[fullhash].js',
    skipped 7 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/webpack/server.ts
    skipped 3 lines
    4 4  import { configCommon, pluginsCommon, srcDir } from './common';
    5 5  import { Configuration } from 'webpack';
    6 6  import { WebpackConfigOptions } from './config';
     7 +import nodeExternals from 'webpack-node-externals';
    7 8   
    8 9  const distDir = 'dist';
    9 10   
    skipped 3 lines
    13 14  }) => ({
    14 15   entry: join(__dirname, '..', srcDir, 'server.tsx'),
    15 16   ...configCommon(mode),
     17 + devtool: mode === 'production' ? false : 'inline-cheap-module-source-map',
    16 18   module: {
    17 19   rules: [
    18 20   {
    skipped 50 lines
    69 71   minimize: false,
    70 72   },
    71 73   target: 'node',
     74 + externals: ['react-helmet', nodeExternals() as any],
    72 75   watch,
    73 76  });
    74 77   
  • ■ ■ ■ ■
    packages/web/webpack/start_dev.sh
    skipped 18 lines
    19 19  done
    20 20   
    21 21  echo "Starting server bundle"
    22  -nodemon --watch dist/main.js -V dist/main.js 2>&1 &
     22 +nodemon --watch dist/main.js -V --inspect=9203 dist/main.js 2>&1 &
    23 23  SRV_PID=$!
    24 24   
    25 25  # Some magic to shut down all services at once when requested
    skipped 33 lines
  • ■ ■ ■ ■ ■ ■
    yarn.lock
    skipped 3902 lines
    3903 3903   resolved "https://registry.yarnpkg.com/@types/react-gtm-module/-/react-gtm-module-2.0.1.tgz#b2c6cd14ec251d6ae7fa576edf1d43825908a378"
    3904 3904   integrity sha512-T/DN9gAbCYk5wJ1nxf4pSwmXz4d1iVjM++OoG+mwMfz9STMAotGjSb65gJHOS5bPvl6vLSsJnuC+y/43OQrltg==
    3905 3905   
     3906 +"@types/react-helmet@^6.1.5":
     3907 + version "6.1.5"
     3908 + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083"
     3909 + integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA==
     3910 + dependencies:
     3911 + "@types/react" "*"
     3912 + 
    3906 3913  "@types/[email protected]":
    3907 3914   version "11.0.5"
    3908 3915   resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087"
    skipped 115 lines
    4024 4031   version "1.16.3"
    4025 4032   resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.3.tgz#b776327a73e561b71e7881d0cd6d34a1424db86a"
    4026 4033   integrity sha512-9gtOPPkfyNoEqCQgx4qJKkuNm/x0R2hKR7fdl7zvTJyHnIisuE/LfvXOsYWL0o3qq6uiBnKZNNNzi3l0y/X+xw==
     4034 + 
     4035 +"@types/webpack-node-externals@^2.5.3":
     4036 + version "2.5.3"
     4037 + resolved "https://registry.yarnpkg.com/@types/webpack-node-externals/-/webpack-node-externals-2.5.3.tgz#921783aadda1fe686db0a70e20e4b9548b5a3cef"
     4038 + integrity sha512-A9JxaR8QXoYT95egET4AmCFuChyTlP8d18ZAnmSHuIMsFdS7QlCQQ8pmN/+FHgLIkm+ViE/VngltT5avLACY9A==
     4039 + dependencies:
     4040 + "@types/node" "*"
     4041 + webpack "^5"
    4027 4042   
    4028 4043  "@types/webpack-sources@*":
    4029 4044   version "3.2.0"
    skipped 2985 lines
    7015 7030   version "5.9.2"
    7016 7031   resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz#0224dcd6a43389ebfb2d55efee517e5466772dd9"
    7017 7032   integrity sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==
     7033 + dependencies:
     7034 + graceful-fs "^4.2.4"
     7035 + tapable "^2.2.0"
     7036 + 
     7037 +enhanced-resolve@^5.10.0:
     7038 + version "5.10.0"
     7039 + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6"
     7040 + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==
    7018 7041   dependencies:
    7019 7042   graceful-fs "^4.2.4"
    7020 7043   tapable "^2.2.0"
    skipped 3066 lines
    10087 10110   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
    10088 10111   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
    10089 10112   
    10090  -json-parse-even-better-errors@^2.3.0:
     10113 +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
    10091 10114   version "2.3.1"
    10092 10115   resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
    10093 10116   integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
    skipped 2371 lines
    12465 12488   is-plain-object "5.0.0"
    12466 12489   react-is "17.0.2"
    12467 12490   
    12468  -react-fast-compare@^3.0.1, react-fast-compare@^3.2.0:
     12491 +react-fast-compare@^3.0.1, react-fast-compare@^3.1.1, react-fast-compare@^3.2.0:
    12469 12492   version "3.2.0"
    12470 12493   resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
    12471 12494   integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
    skipped 13 lines
    12485 12508   prop-types "^15.7.2"
    12486 12509   react-fast-compare "^3.2.0"
    12487 12510   shallowequal "^1.1.0"
     12511 + 
     12512 +react-helmet@^6.1.0:
     12513 + version "6.1.0"
     12514 + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726"
     12515 + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==
     12516 + dependencies:
     12517 + object-assign "^4.1.1"
     12518 + prop-types "^15.7.2"
     12519 + react-fast-compare "^3.1.1"
     12520 + react-side-effect "^2.1.0"
    12488 12521   
    12489 12522  react-hook-form@^7.15.3:
    12490 12523   version "7.29.0"
    skipped 72 lines
    12563 12596   integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
    12564 12597   dependencies:
    12565 12598   history "^5.2.0"
     12599 + 
     12600 +react-side-effect@^2.1.0:
     12601 + version "2.1.2"
     12602 + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a"
     12603 + integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==
    12566 12604   
    12567 12605  react-sizeme@^3.0.1:
    12568 12606   version "3.0.2"
    skipped 2514 lines
    15083 15121   glob-to-regexp "^0.4.1"
    15084 15122   graceful-fs "^4.1.2"
    15085 15123   
     15124 +watchpack@^2.4.0:
     15125 + version "2.4.0"
     15126 + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
     15127 + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
     15128 + dependencies:
     15129 + glob-to-regexp "^0.4.1"
     15130 + graceful-fs "^4.1.2"
     15131 + 
    15086 15132  wbuf@^1.1.0, wbuf@^1.7.3:
    15087 15133   version "1.7.3"
    15088 15134   resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
    skipped 140 lines
    15229 15275   clone-deep "^4.0.1"
    15230 15276   wildcard "^2.0.0"
    15231 15277   
     15278 +webpack-node-externals@^3.0.0:
     15279 + version "3.0.0"
     15280 + resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917"
     15281 + integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==
     15282 + 
    15232 15283  webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
    15233 15284   version "1.4.3"
    15234 15285   resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
    skipped 6 lines
    15241 15292   version "3.2.3"
    15242 15293   resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
    15243 15294   integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
     15295 + 
     15296 +webpack-stats-plugin@^1.1.0:
     15297 + version "1.1.0"
     15298 + resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-1.1.0.tgz#32c62ba306ffb8c7fcc5d3580b09311fa8ff897a"
     15299 + integrity sha512-D0meHk1WYryUbuCnWJuomJFAYvqs0rxv/JFu1XJT1YYpczdgnP1/vz+u/5Z31jrTxT6dJSxCg+TuKTgjhoZS6g==
    15244 15300   
    15245 15301  webpack-virtual-modules@^0.2.2:
    15246 15302   version "0.2.2"
    skipped 35 lines
    15282 15338   terser-webpack-plugin "^1.4.3"
    15283 15339   watchpack "^1.7.4"
    15284 15340   webpack-sources "^1.4.1"
     15341 + 
     15342 +webpack@^5:
     15343 + version "5.74.0"
     15344 + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980"
     15345 + integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==
     15346 + dependencies:
     15347 + "@types/eslint-scope" "^3.7.3"
     15348 + "@types/estree" "^0.0.51"
     15349 + "@webassemblyjs/ast" "1.11.1"
     15350 + "@webassemblyjs/wasm-edit" "1.11.1"
     15351 + "@webassemblyjs/wasm-parser" "1.11.1"
     15352 + acorn "^8.7.1"
     15353 + acorn-import-assertions "^1.7.6"
     15354 + browserslist "^4.14.5"
     15355 + chrome-trace-event "^1.0.2"
     15356 + enhanced-resolve "^5.10.0"
     15357 + es-module-lexer "^0.9.0"
     15358 + eslint-scope "5.1.1"
     15359 + events "^3.2.0"
     15360 + glob-to-regexp "^0.4.1"
     15361 + graceful-fs "^4.2.9"
     15362 + json-parse-even-better-errors "^2.3.1"
     15363 + loader-runner "^4.2.0"
     15364 + mime-types "^2.1.27"
     15365 + neo-async "^2.6.2"
     15366 + schema-utils "^3.1.0"
     15367 + tapable "^2.1.1"
     15368 + terser-webpack-plugin "^5.1.3"
     15369 + watchpack "^2.4.0"
     15370 + webpack-sources "^3.2.3"
    15285 15371   
    15286 15372  webpack@^5.52.1, webpack@^5.9.0:
    15287 15373   version "5.70.0"
    skipped 337 lines
Please wait...
Page is in error, reload to recover